From 70ff2456c956cf659f0d3284dc0a50b91652af1b Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 4 Mar 2024 14:13:12 +0000 Subject: [PATCH 01/10] Add support for WASILibc Dispatch is not supported on WASI, and only Unix domain sockets are supported, which means we have to exclude those APIs on this platform. --- Package.swift | 5 +++ Sources/CNIOSHA1/c_nio_sha1.c | 2 +- Sources/CNIOWASI/include/CNIOWASI.h | 12 ++++++ Sources/NIOConcurrencyHelpers/NIOLock.swift | 10 +++-- Sources/NIOConcurrencyHelpers/atomics.swift | 2 + Sources/NIOConcurrencyHelpers/lock.swift | 26 +++++++----- Sources/NIOCore/AsyncAwaitSupport.swift | 2 + Sources/NIOCore/BSDSocketAPI.swift | 16 +++++++- Sources/NIOCore/ByteBuffer-aux.swift | 11 ++++- Sources/NIOCore/ByteBuffer-conversions.swift | 4 ++ Sources/NIOCore/ByteBuffer-core.swift | 2 + Sources/NIOCore/Channel.swift | 2 + .../NIOCore/ConvenienceOptionSupport.swift | 2 + .../NIOCore/DispatchQueue+WithFuture.swift | 2 + Sources/NIOCore/EventLoop.swift | 21 ++++++++-- Sources/NIOCore/EventLoopFuture.swift | 4 ++ Sources/NIOCore/FileHandle.swift | 6 +++ Sources/NIOCore/FileRegion.swift | 2 + Sources/NIOCore/GlobalSingletons.swift | 2 + Sources/NIOCore/IO.swift | 2 + Sources/NIOCore/Interfaces.swift | 14 +++++-- Sources/NIOCore/MulticastChannel.swift | 8 ++-- Sources/NIOCore/SocketAddresses.swift | 41 ++++++++++++++++++- Sources/NIOCore/SocketOptionProvider.swift | 4 ++ Sources/NIOCore/SystemCallHelpers.swift | 14 ++++++- Sources/NIOCore/Utilities.swift | 6 ++- .../NIOEmbedded/AsyncTestingEventLoop.swift | 2 + Sources/NIOEmbedded/Embedded.swift | 2 + Sources/NIOPosix/GetaddrinfoResolver.swift | 2 + .../MultiThreadedEventLoopGroup.swift | 2 + Sources/NIOPosix/NIOThreadPool.swift | 2 + Sources/_NIODataStructures/Heap.swift | 2 + 32 files changed, 198 insertions(+), 36 deletions(-) create mode 100644 Sources/CNIOWASI/include/CNIOWASI.h diff --git a/Package.swift b/Package.swift index 3d0914bab5..f31946cfbf 100644 --- a/Package.swift +++ b/Package.swift @@ -58,6 +58,7 @@ let package = Package( "CNIODarwin", "CNIOLinux", "CNIOWindows", + "CNIOWASI", "_NIODataStructures", swiftCollections, swiftAtomics, @@ -142,6 +143,10 @@ let package = Package( name: "CNIOWindows", dependencies: [] ), + .target( + name: "CNIOWASI", + dependencies: [] + ), .target( name: "NIOConcurrencyHelpers", dependencies: [ diff --git a/Sources/CNIOSHA1/c_nio_sha1.c b/Sources/CNIOSHA1/c_nio_sha1.c index 30b61c8793..2e4fa45bb1 100644 --- a/Sources/CNIOSHA1/c_nio_sha1.c +++ b/Sources/CNIOSHA1/c_nio_sha1.c @@ -54,7 +54,7 @@ #endif #ifdef __ANDROID__ #include -#elif defined(__linux__) || defined(__APPLE__) +#elif defined(__linux__) || defined(__APPLE__) || defined(__wasm32__) #include #endif diff --git a/Sources/CNIOWASI/include/CNIOWASI.h b/Sources/CNIOWASI/include/CNIOWASI.h new file mode 100644 index 0000000000..2582e3f8f3 --- /dev/null +++ b/Sources/CNIOWASI/include/CNIOWASI.h @@ -0,0 +1,12 @@ +#pragma once + +#if __wasi__ + +#include + +static inline void CNIOWASI_gettime(struct timespec *tv) { + // ClangImporter doesn't support `CLOCK_MONOTONIC` declaration in WASILibc, thus we have to define a bridge manually + clock_gettime(CLOCK_MONOTONIC, tv); +} + +#endif \ No newline at end of file diff --git a/Sources/NIOConcurrencyHelpers/NIOLock.swift b/Sources/NIOConcurrencyHelpers/NIOLock.swift index db1a8f9811..8daaca7670 100644 --- a/Sources/NIOConcurrencyHelpers/NIOLock.swift +++ b/Sources/NIOConcurrencyHelpers/NIOLock.swift @@ -21,6 +21,8 @@ import WinSDK import Glibc #elseif canImport(Musl) import Musl +#elseif canImport(WASILibc) +import WASILibc #else #error("The concurrency NIOLock module was unable to identify your C library.") #endif @@ -43,7 +45,7 @@ extension LockOperations { #if os(Windows) InitializeSRWLock(mutex) -#else +#elseif !os(WASI) var attr = pthread_mutexattr_t() pthread_mutexattr_init(&attr) debugOnly { @@ -61,7 +63,7 @@ extension LockOperations { #if os(Windows) // SRWLOCK does not need to be free'd -#else +#elseif !os(WASI) let err = pthread_mutex_destroy(mutex) precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)") #endif @@ -73,7 +75,7 @@ extension LockOperations { #if os(Windows) AcquireSRWLockExclusive(mutex) -#else +#elseif !os(WASI) let err = pthread_mutex_lock(mutex) precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)") #endif @@ -85,7 +87,7 @@ extension LockOperations { #if os(Windows) ReleaseSRWLockExclusive(mutex) -#else +#elseif !os(WASI) let err = pthread_mutex_unlock(mutex) precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)") #endif diff --git a/Sources/NIOConcurrencyHelpers/atomics.swift b/Sources/NIOConcurrencyHelpers/atomics.swift index 034747cff5..af305caa8a 100644 --- a/Sources/NIOConcurrencyHelpers/atomics.swift +++ b/Sources/NIOConcurrencyHelpers/atomics.swift @@ -30,6 +30,8 @@ fileprivate func sys_sched_yield() { import Glibc #elseif canImport(Musl) import Musl +#elseif canImport(WASILibc) +import WASILibc #else #error("The concurrency atomics module was unable to identify your C library.") #endif diff --git a/Sources/NIOConcurrencyHelpers/lock.swift b/Sources/NIOConcurrencyHelpers/lock.swift index 5df4af7b15..1fa61d381b 100644 --- a/Sources/NIOConcurrencyHelpers/lock.swift +++ b/Sources/NIOConcurrencyHelpers/lock.swift @@ -21,6 +21,8 @@ import WinSDK import Glibc #elseif canImport(Musl) import Musl +#elseif canImport(WASILibc) +import WASILibc #else #error("The concurrency lock module was unable to identify your C library.") #endif @@ -45,7 +47,7 @@ public final class Lock { public init() { #if os(Windows) InitializeSRWLock(self.mutex) -#else +#elseif !os(WASI) var attr = pthread_mutexattr_t() pthread_mutexattr_init(&attr) debugOnly { @@ -60,7 +62,7 @@ public final class Lock { deinit { #if os(Windows) // SRWLOCK does not need to be free'd -#else +#elseif !os(WASI) let err = pthread_mutex_destroy(self.mutex) precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)") #endif @@ -74,7 +76,7 @@ public final class Lock { public func lock() { #if os(Windows) AcquireSRWLockExclusive(self.mutex) -#else +#elseif !os(WASI) let err = pthread_mutex_lock(self.mutex) precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)") #endif @@ -87,7 +89,7 @@ public final class Lock { public func unlock() { #if os(Windows) ReleaseSRWLockExclusive(self.mutex) -#else +#elseif !os(WASI) let err = pthread_mutex_unlock(self.mutex) precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)") #endif @@ -127,7 +129,7 @@ public final class ConditionLock { #if os(Windows) private let cond: UnsafeMutablePointer = UnsafeMutablePointer.allocate(capacity: 1) -#else +#elseif !os(WASI) private let cond: UnsafeMutablePointer = UnsafeMutablePointer.allocate(capacity: 1) #endif @@ -140,7 +142,7 @@ public final class ConditionLock { self.mutex = NIOLock() #if os(Windows) InitializeConditionVariable(self.cond) -#else +#elseif !os(WASI) let err = pthread_cond_init(self.cond, nil) precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)") #endif @@ -149,11 +151,13 @@ public final class ConditionLock { deinit { #if os(Windows) // condition variables do not need to be explicitly destroyed -#else +#elseif !os(WASI) let err = pthread_cond_destroy(self.cond) precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)") #endif +#if !os(WASI) self.cond.deallocate() +#endif } /// Acquire the lock, regardless of the value of the state variable. @@ -193,7 +197,7 @@ public final class ConditionLock { #if os(Windows) let result = SleepConditionVariableSRW(self.cond, mutex, INFINITE, 0) precondition(result, "\(#function) failed in SleepConditionVariableSRW with error \(GetLastError())") -#else +#elseif !os(WASI) let err = pthread_cond_wait(self.cond, mutex) precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)") #endif @@ -235,6 +239,8 @@ public final class ConditionLock { // NOTE: this may be a spurious wakeup, adjust the timeout accordingly dwMilliseconds = dwMilliseconds - (timeGetTime() - dwWaitStart) } +#elseif os(WASI) + return true #else let nsecPerSec: Int64 = 1000000000 self.lock() @@ -246,7 +252,7 @@ public final class ConditionLock { let allNSecs: Int64 = timeoutNS + Int64(curTime.tv_usec) * 1000 var timeoutAbs = timespec(tv_sec: curTime.tv_sec + Int((allNSecs / nsecPerSec)), - tv_nsec: Int(allNSecs % nsecPerSec)) + tv_nsec: Int(allNSecs % nsecPerSec)) assert(timeoutAbs.tv_nsec >= 0 && timeoutAbs.tv_nsec < Int(nsecPerSec)) assert(timeoutAbs.tv_sec >= curTime.tv_sec) return self.mutex.withLockPrimitive { mutex -> Bool in @@ -277,7 +283,7 @@ public final class ConditionLock { self.unlock() #if os(Windows) WakeAllConditionVariable(self.cond) -#else +#elseif !os(WASI) let err = pthread_cond_broadcast(self.cond) precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)") #endif diff --git a/Sources/NIOCore/AsyncAwaitSupport.swift b/Sources/NIOCore/AsyncAwaitSupport.swift index 659df1ad70..f498ea9efd 100644 --- a/Sources/NIOCore/AsyncAwaitSupport.swift +++ b/Sources/NIOCore/AsyncAwaitSupport.swift @@ -33,6 +33,7 @@ extension EventLoopFuture { } } +#if !os(WASI) extension EventLoopGroup { /// Shuts down the event loop gracefully. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) @@ -49,6 +50,7 @@ extension EventLoopGroup { } } } +#endif extension EventLoopPromise { /// Complete a future with the result (or error) of the `async` function `body`. diff --git a/Sources/NIOCore/BSDSocketAPI.swift b/Sources/NIOCore/BSDSocketAPI.swift index 07ba7bebcb..a42400815f 100644 --- a/Sources/NIOCore/BSDSocketAPI.swift +++ b/Sources/NIOCore/BSDSocketAPI.swift @@ -80,6 +80,8 @@ import Darwin private let sysInet_ntop: @convention(c) (CInt, UnsafeRawPointer?, UnsafeMutablePointer?, socklen_t) -> UnsafePointer? = inet_ntop private let sysInet_pton: @convention(c) (CInt, UnsafePointer?, UnsafeMutableRawPointer?) -> CInt = inet_pton +#elseif canImport(WASILibc) +import WASILibc #else #error("The BSD Socket module was unable to identify your C library.") #endif @@ -191,6 +193,11 @@ extension NIOBSDSocket.AddressFamily { // Protocol Family extension NIOBSDSocket.ProtocolFamily { +#if os(WASI) + /// UNIX local to the host. + public static let unix: NIOBSDSocket.ProtocolFamily = + NIOBSDSocket.ProtocolFamily(rawValue: 1) +#else /// IP network 4 protocol. public static let inet: NIOBSDSocket.ProtocolFamily = NIOBSDSocket.ProtocolFamily(rawValue: PF_INET) @@ -202,9 +209,10 @@ extension NIOBSDSocket.ProtocolFamily { /// UNIX local to the host. public static let unix: NIOBSDSocket.ProtocolFamily = NIOBSDSocket.ProtocolFamily(rawValue: PF_UNIX) +#endif } -#if !os(Windows) +#if !os(Windows) && !os(WASI) extension NIOBSDSocket.ProtocolFamily { /// UNIX local to the host, alias for `PF_UNIX` (`.unix`) public static let local: NIOBSDSocket.ProtocolFamily = @@ -370,6 +378,7 @@ extension NIOBSDSocket.Option { public static let mptcp_info = NIOBSDSocket.Option(rawValue: 1) } +#if !os(WASI) // Socket Options extension NIOBSDSocket.Option { /// Get the error status and clear. @@ -396,8 +405,9 @@ extension NIOBSDSocket.Option { public static let so_reuseaddr: NIOBSDSocket.Option = NIOBSDSocket.Option(rawValue: SO_REUSEADDR) } +#endif -#if !os(Windows) +#if !os(Windows) && !os(WASI) extension NIOBSDSocket.Option { /// Indicate when to generate timestamps. public static let so_timestamp: NIOBSDSocket.Option = @@ -405,6 +415,7 @@ extension NIOBSDSocket.Option { } #endif +#if !os(WASI) extension NIOBSDSocket { // Sadly this was defined on BSDSocket, and we need it for SocketAddress. @inline(never) @@ -444,3 +455,4 @@ extension NIOBSDSocket { #endif } } +#endif diff --git a/Sources/NIOCore/ByteBuffer-aux.swift b/Sources/NIOCore/ByteBuffer-aux.swift index 7765394651..34da656d97 100644 --- a/Sources/NIOCore/ByteBuffer-aux.swift +++ b/Sources/NIOCore/ByteBuffer-aux.swift @@ -12,7 +12,9 @@ // //===----------------------------------------------------------------------===// +#if canImport(Dispatch) import Dispatch +#endif import _NIOBase64 extension ByteBuffer { @@ -267,7 +269,8 @@ extension ByteBuffer { return self.setString(String(substring), at: index) } } - + +#if canImport(Dispatch) // MARK: DispatchData APIs /// Write `dispatchData` into this `ByteBuffer`, moving the writer index forward appropriately. /// @@ -332,7 +335,7 @@ extension ByteBuffer { self._moveReaderIndex(forwardBy: length) return result } - +#endif // MARK: Other APIs @@ -660,6 +663,7 @@ extension ByteBuffer { self = ByteBufferAllocator().buffer(buffer: buffer) } +#if canImport(Dispatch) /// Create a fresh `ByteBuffer` containing the bytes contained in the given `DispatchData`. /// /// This will allocate a new `ByteBuffer` with enough space to fit the bytes of the `DispatchData` and potentially @@ -674,6 +678,7 @@ extension ByteBuffer { public init(dispatchData: DispatchData) { self = ByteBufferAllocator().buffer(dispatchData: dispatchData) } +#endif } extension ByteBuffer: Codable { @@ -785,6 +790,7 @@ extension ByteBufferAllocator { return newBuffer } +#if canImport(Dispatch) /// Create a fresh `ByteBuffer` containing the bytes contained in the given `DispatchData`. /// /// This will allocate a new `ByteBuffer` with enough space to fit the bytes of the `DispatchData` and potentially @@ -797,6 +803,7 @@ extension ByteBufferAllocator { buffer.writeDispatchData(dispatchData) return buffer } +#endif } diff --git a/Sources/NIOCore/ByteBuffer-conversions.swift b/Sources/NIOCore/ByteBuffer-conversions.swift index ef197aefe3..92dd3d9445 100644 --- a/Sources/NIOCore/ByteBuffer-conversions.swift +++ b/Sources/NIOCore/ByteBuffer-conversions.swift @@ -12,7 +12,9 @@ // //===----------------------------------------------------------------------===// +#if canImport(Dispatch) import Dispatch +#endif extension Array where Element == UInt8 { @@ -48,6 +50,7 @@ extension String { } } +#if canImport(Dispatch) extension DispatchData { /// Creates a `DispatchData` from a given `ByteBuffer`. The entire readable portion of the buffer will be read. @@ -59,3 +62,4 @@ extension DispatchData { } } +#endif diff --git a/Sources/NIOCore/ByteBuffer-core.swift b/Sources/NIOCore/ByteBuffer-core.swift index 96ad63c028..83c6c8d8da 100644 --- a/Sources/NIOCore/ByteBuffer-core.swift +++ b/Sources/NIOCore/ByteBuffer-core.swift @@ -20,6 +20,8 @@ import Darwin import Glibc #elseif canImport(Musl) import Musl +#elseif canImport(WASILibc) +import WASILibc #else #error("The Byte Buffer module was unable to identify your C library.") #endif diff --git a/Sources/NIOCore/Channel.swift b/Sources/NIOCore/Channel.swift index d61e3b7dcf..d6abddaafb 100644 --- a/Sources/NIOCore/Channel.swift +++ b/Sources/NIOCore/Channel.swift @@ -362,9 +362,11 @@ public enum ChannelError: Error { /// address. case illegalMulticastAddress(SocketAddress) +#if !os(WASI) /// Multicast is not supported on Interface @available(*, deprecated, renamed: "NIOMulticastNotSupportedError") case multicastNotSupported(NIONetworkInterface) +#endif /// An operation that was inappropriate given the current `Channel` state was attempted. case inappropriateOperationForState diff --git a/Sources/NIOCore/ConvenienceOptionSupport.swift b/Sources/NIOCore/ConvenienceOptionSupport.swift index af93b79803..74796dbb50 100644 --- a/Sources/NIOCore/ConvenienceOptionSupport.swift +++ b/Sources/NIOCore/ConvenienceOptionSupport.swift @@ -173,9 +173,11 @@ extension ChannelOptions { mutating func applyFallbackMapping(_ universalBootstrap: NIOClientTCPBootstrap) -> NIOClientTCPBootstrap { var result = universalBootstrap +#if !os(WASI) if self.consumeAllowLocalEndpointReuse().isSet { result = result.channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1) } +#endif if self.consumeAllowRemoteHalfClosure().isSet { result = result.channelOption(ChannelOptions.allowRemoteHalfClosure, value: true) } diff --git a/Sources/NIOCore/DispatchQueue+WithFuture.swift b/Sources/NIOCore/DispatchQueue+WithFuture.swift index f85ea96dbf..593380aa26 100644 --- a/Sources/NIOCore/DispatchQueue+WithFuture.swift +++ b/Sources/NIOCore/DispatchQueue+WithFuture.swift @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#if canImport(Dispatch) import Dispatch extension DispatchQueue { @@ -45,3 +46,4 @@ extension DispatchQueue { return promise.futureResult } } +#endif diff --git a/Sources/NIOCore/EventLoop.swift b/Sources/NIOCore/EventLoop.swift index 50b90ed925..7e0f2ffdf6 100644 --- a/Sources/NIOCore/EventLoop.swift +++ b/Sources/NIOCore/EventLoop.swift @@ -13,7 +13,12 @@ //===----------------------------------------------------------------------===// import NIOConcurrencyHelpers +#if canImport(Dispatch) import Dispatch +#elseif canImport(WASILibc) +import WASILibc +import CNIOWASI +#endif #if os(Linux) import CNIOLinux #endif // os(Linux) @@ -595,7 +600,6 @@ public struct NIODeadline: Equatable, Hashable, Sendable { self._uptimeNanoseconds = nanoseconds } - /// Getting the time is a very common operation so it warrants optimization. /// /// Prior to this function, NIO relied on `DispatchTime.now()`, on all platforms. In addition to @@ -615,7 +619,14 @@ public struct NIODeadline: Equatable, Hashable, Sendable { /// and the odds that this code will still be running 530 years from now is very, very low, /// so as a practical matter this will never overflow. return UInt64(ts.tv_sec) &* 1_000_000_000 &+ UInt64(ts.tv_nsec) -#else // os(Linux) +#elseif os(WASI) + var ts = timespec() + CNIOWASI_gettime(&ts) + /// We use unsafe arithmetic here because `UInt64.max` nanoseconds is more than 580 years, + /// and the odds that this code will still be running 530 years from now is very, very low, + /// so as a practical matter this will never overflow. + return UInt64(ts.tv_sec) &* 1_000_000_000 &+ UInt64(ts.tv_nsec) +#else return DispatchTime.now().uptimeNanoseconds #endif // os(Linux) } @@ -1127,12 +1138,14 @@ public protocol EventLoopGroup: AnyObject, _NIOPreconcurrencySendable { /// The rule of thumb is: If you are trying to do _load balancing_, use `next()`. If you just want to create a new /// future or kick off some operation, use `any()`. func any() -> EventLoop - + +#if canImport(Dispatch) /// Shuts down the eventloop gracefully. This function is clearly an outlier in that it uses a completion /// callback instead of an EventLoopFuture. The reason for that is that NIO's EventLoopFutures will call back on an event loop. /// The virtue of this function is to shut the event loop down. To work around that we call back on a DispatchQueue /// instead. @preconcurrency func shutdownGracefully(queue: DispatchQueue, _ callback: @Sendable @escaping (Error?) -> Void) +#endif /// Returns an `EventLoopIterator` over the `EventLoop`s in this `EventLoopGroup`. /// @@ -1154,6 +1167,7 @@ extension EventLoopGroup { } } +#if canImport(Dispatch) extension EventLoopGroup { @preconcurrency public func shutdownGracefully(_ callback: @escaping @Sendable (Error?) -> Void) { self.shutdownGracefully(queue: .global(), callback) @@ -1188,6 +1202,7 @@ extension EventLoopGroup { return } } +#endif /// This type is intended to be used by libraries which use NIO, and offer their users either the option /// to `.share` an existing event loop group or create (and manage) a new one (`.createNew`) and let it be diff --git a/Sources/NIOCore/EventLoopFuture.swift b/Sources/NIOCore/EventLoopFuture.swift index 185e00fecc..bae6625f3a 100644 --- a/Sources/NIOCore/EventLoopFuture.swift +++ b/Sources/NIOCore/EventLoopFuture.swift @@ -13,7 +13,9 @@ //===----------------------------------------------------------------------===// import NIOConcurrencyHelpers +#if canImport(Dispatch) import Dispatch +#endif /// Internal list of callbacks. /// @@ -1625,6 +1627,7 @@ extension EventLoopFuture { // MARK: may block +#if canImport(Dispatch) extension EventLoopFuture { /// Chain an `EventLoopFuture` providing the result of a IO / task that may block. For example: /// @@ -1719,6 +1722,7 @@ extension EventLoopFuture { } } } +#endif // MARK: assertion diff --git a/Sources/NIOCore/FileHandle.swift b/Sources/NIOCore/FileHandle.swift index b9de95175a..b063f3c266 100644 --- a/Sources/NIOCore/FileHandle.swift +++ b/Sources/NIOCore/FileHandle.swift @@ -19,6 +19,8 @@ import Darwin import Glibc #elseif canImport(Musl) import Musl +#elseif canImport(WASILibc) +import WASILibc #else #error("The File Handle module was unable to identify your C library.") #endif @@ -54,6 +56,7 @@ public final class NIOFileHandle: FileDescriptor { assert(!self.isOpen, "leaked open NIOFileHandle(descriptor: \(self.descriptor)). Call `close()` to close or `takeDescriptorOwnership()` to take ownership and close by some other means.") } +#if !os(WASI) /// Duplicates this `NIOFileHandle`. This means that a new `NIOFileHandle` object with a new underlying file descriptor /// is returned. The caller takes ownership of the returned `NIOFileHandle` and is responsible for closing it. /// @@ -65,6 +68,7 @@ public final class NIOFileHandle: FileDescriptor { NIOFileHandle(descriptor: try SystemCalls.dup(descriptor: fd)) } } +#endif /// Take the ownership of the underlying file descriptor. This is similar to `close()` but the underlying file /// descriptor remains open. The caller is responsible for closing the file descriptor by some other means. @@ -138,6 +142,7 @@ extension NIOFileHandle { public static let defaultPermissions = S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH #endif +#if !os(WASI) /// Allows file creation when opening file for writing. File owner is set to the effective user ID of the process. /// /// - parameters: @@ -145,6 +150,7 @@ extension NIOFileHandle { public static func allowFileCreation(posixMode: NIOPOSIXFileMode = defaultPermissions) -> Flags { return Flags(posixMode: posixMode, posixFlags: O_CREAT) } +#endif /// Allows the specification of POSIX flags (e.g. `O_TRUNC`) and mode (e.g. `S_IWUSR`) /// diff --git a/Sources/NIOCore/FileRegion.swift b/Sources/NIOCore/FileRegion.swift index 72db1b13a7..c5852e7f78 100644 --- a/Sources/NIOCore/FileRegion.swift +++ b/Sources/NIOCore/FileRegion.swift @@ -19,6 +19,8 @@ import Darwin import Glibc #elseif canImport(Musl) import Musl +#elseif canImport(WASILibc) +import WASILibc #else #error("The File Region module was unable to identify your C library.") #endif diff --git a/Sources/NIOCore/GlobalSingletons.swift b/Sources/NIOCore/GlobalSingletons.swift index 79e752a74e..2f86c55329 100644 --- a/Sources/NIOCore/GlobalSingletons.swift +++ b/Sources/NIOCore/GlobalSingletons.swift @@ -22,6 +22,8 @@ import WinSDK import Glibc #elseif canImport(Musl) import Musl +#elseif canImport(WASILibc) +import WASILibc #else #error("Unsupported C library") #endif diff --git a/Sources/NIOCore/IO.swift b/Sources/NIOCore/IO.swift index 3fdd7a10f9..20097054ab 100644 --- a/Sources/NIOCore/IO.swift +++ b/Sources/NIOCore/IO.swift @@ -32,6 +32,8 @@ internal func MAKELANGID(_ p: WORD, _ s: WORD) -> DWORD { import Glibc #elseif canImport(Musl) import Musl +#elseif canImport(WASILibc) +import WASILibc #elseif canImport(Darwin) import Darwin #else diff --git a/Sources/NIOCore/Interfaces.swift b/Sources/NIOCore/Interfaces.swift index c4b6eb001c..2c7c7d3e1d 100644 --- a/Sources/NIOCore/Interfaces.swift +++ b/Sources/NIOCore/Interfaces.swift @@ -20,6 +20,8 @@ import Musl import CNIOLinux #elseif canImport(Darwin) import Darwin +#elseif canImport(WASILibc) +import WASILibc #elseif os(Windows) import let WinSDK.AF_INET import let WinSDK.AF_INET6 @@ -42,7 +44,7 @@ import typealias WinSDK.UINT8 #error("The Core interfaces module was unable to identify your C library.") #endif -#if !os(Windows) +#if !os(Windows) && !os(WASI) private extension ifaddrs { var dstaddr: UnsafeMutablePointer? { #if os(Linux) || os(Android) @@ -62,6 +64,7 @@ private extension ifaddrs { } #endif +#if !os(WASI) /// A representation of a single network interface on a system. @available(*, deprecated, renamed: "NIONetworkDevice") public final class NIONetworkInterface: Sendable { @@ -186,6 +189,7 @@ extension NIONetworkInterface: Equatable { lhs.interfaceIndex == rhs.interfaceIndex } } +#endif /// A helper extension for working with sockaddr pointers. extension UnsafeMutablePointer where Pointee == sockaddr { @@ -193,10 +197,12 @@ extension UnsafeMutablePointer where Pointee == sockaddr { fileprivate func convert() -> SocketAddress? { let addressBytes = UnsafeRawPointer(self) switch NIOBSDSocket.AddressFamily(rawValue: CInt(pointee.sa_family)) { +#if !os(WASI) case .inet: return SocketAddress(addressBytes.load(as: sockaddr_in.self)) case .inet6: return SocketAddress(addressBytes.load(as: sockaddr_in6.self)) +#endif case .unix: return SocketAddress(addressBytes.load(as: sockaddr_un.self)) default: @@ -302,7 +308,7 @@ public struct NIONetworkDevice { } self.backing = backing } -#else +#elseif !os(WASI) internal init?(_ caddr: ifaddrs) { guard let backing = Backing(caddr) else { return nil @@ -312,7 +318,7 @@ public struct NIONetworkDevice { } #endif -#if !os(Windows) +#if !os(Windows) && !os(WASI) /// Convert a `NIONetworkInterface` to a `NIONetworkDevice`. As `NIONetworkDevice`s are a superset of `NIONetworkInterface`s, /// it is always possible to perform this conversion. @available(*, deprecated, message: "This is a compatibility helper, and will be removed in a future release") @@ -412,7 +418,7 @@ extension NIONetworkDevice { self.pointToPointDestinationAddress = nil self.multicastSupported = false } -#else +#elseif !os(WASI) internal init?(_ caddr: ifaddrs) { self.name = String(cString: caddr.ifa_name!) self.address = caddr.ifa_addr.flatMap { $0.convert() } diff --git a/Sources/NIOCore/MulticastChannel.swift b/Sources/NIOCore/MulticastChannel.swift index 9669617d3c..6d2137cd35 100644 --- a/Sources/NIOCore/MulticastChannel.swift +++ b/Sources/NIOCore/MulticastChannel.swift @@ -25,7 +25,7 @@ public protocol MulticastChannel: Channel { /// `nil` if you are not interested in the result of the operation. func joinGroup(_ group: SocketAddress, promise: EventLoopPromise?) -#if !os(Windows) +#if !os(Windows) && !os(WASI) /// Request that the `MulticastChannel` join the multicast group given by `group` on the interface /// given by `interface`. /// @@ -56,7 +56,7 @@ public protocol MulticastChannel: Channel { /// `nil` if you are not interested in the result of the operation. func leaveGroup(_ group: SocketAddress, promise: EventLoopPromise?) -#if !os(Windows) +#if !os(Windows) && !os(WASI) /// Request that the `MulticastChannel` leave the multicast group given by `group` on the interface /// given by `interface`. /// @@ -93,7 +93,7 @@ extension MulticastChannel { return promise.futureResult } -#if !os(Windows) +#if !os(Windows) && !os(WASI) @available(*, deprecated, renamed: "joinGroup(_:device:)") public func joinGroup(_ group: SocketAddress, interface: NIONetworkInterface?) -> EventLoopFuture { let promise = self.eventLoop.makePromise(of: Void.self) @@ -118,7 +118,7 @@ extension MulticastChannel { return promise.futureResult } -#if !os(Windows) +#if !os(Windows) && !os(WASI) @available(*, deprecated, renamed: "leaveGroup(_:device:)") public func leaveGroup(_ group: SocketAddress, interface: NIONetworkInterface?) -> EventLoopFuture { let promise = self.eventLoop.makePromise(of: Void.self) diff --git a/Sources/NIOCore/SocketAddresses.swift b/Sources/NIOCore/SocketAddresses.swift index a0cf8f0ee5..d0a77dcaa3 100644 --- a/Sources/NIOCore/SocketAddresses.swift +++ b/Sources/NIOCore/SocketAddresses.swift @@ -50,6 +50,8 @@ import Glibc import Musl #endif import CNIOLinux +#elseif canImport(WASILibc) +import WASILibc #else #error("The Socket Addresses module was unable to identify your C library.") #endif @@ -133,6 +135,9 @@ public enum SocketAddress: CustomStringConvertible, Sendable { /// A human-readable description of this `SocketAddress`. Mostly useful for logging. public var description: String { +#if os(WASI) + return "[UDS]" +#else let addressString: String let port: String let host: String? @@ -161,6 +166,7 @@ public enum SocketAddress: CustomStringConvertible, Sendable { } return "[\(type)]\(host.map { "\($0)/\(addressString):" } ?? "\(addressString):")\(port)" +#endif } @available(*, deprecated, renamed: "SocketAddress.protocol") @@ -170,6 +176,9 @@ public enum SocketAddress: CustomStringConvertible, Sendable { /// Returns the protocol family as defined in `man 2 socket` of this `SocketAddress`. public var `protocol`: NIOBSDSocket.ProtocolFamily { +#if os(WASI) + return .unix +#else switch self { case .v4: return .inet @@ -178,8 +187,10 @@ public enum SocketAddress: CustomStringConvertible, Sendable { case .unixDomainSocket: return .unix } +#endif } +#if !os(WASI) /// Get the IP address as a string public var ipAddress: String? { switch self { @@ -195,6 +206,7 @@ public enum SocketAddress: CustomStringConvertible, Sendable { return nil } } +#endif /// Get and set the port associated with the address, if defined. /// When setting to `nil` the port will default to `0` for compatible sockets. The rationale for this is that both `nil` and `0` can @@ -228,9 +240,12 @@ public enum SocketAddress: CustomStringConvertible, Sendable { } } } - + /// Get the pathname of a UNIX domain socket as a string public var pathname: String? { +#if os(WASI) + return nil +#else switch self { case .v4: return nil @@ -246,6 +261,7 @@ public enum SocketAddress: CustomStringConvertible, Sendable { } return pathname } +#endif } /// Calls the given function with a pointer to a `sockaddr` structure and the associated size @@ -279,6 +295,7 @@ public enum SocketAddress: CustomStringConvertible, Sendable { self = .v6(.init(address: addr, host: host)) } +#if !os(WASI) /// Creates a new IPv4 `SocketAddress`. /// /// - parameters: @@ -294,6 +311,7 @@ public enum SocketAddress: CustomStringConvertible, Sendable { public init(_ addr: sockaddr_in6) { self = .v6(.init(address: addr, host: addr.addressDescription())) } +#endif /// Creates a new Unix Domain Socket `SocketAddress`. /// @@ -319,15 +337,18 @@ public enum SocketAddress: CustomStringConvertible, Sendable { var addr = sockaddr_un() addr.sun_family = sa_family_t(NIOBSDSocket.AddressFamily.unix.rawValue) +#if !os(WASI) pathBytes.withUnsafeBytes { srcBuffer in withUnsafeMutableBytes(of: &addr.sun_path) { dstPtr in dstPtr.copyMemory(from: srcBuffer) } } +#endif self = .unixDomainSocket(.init(address: addr)) } +#if !os(WASI) /// Create a new `SocketAddress` for an IP address in string form. /// /// - parameters: @@ -371,6 +392,7 @@ public enum SocketAddress: CustomStringConvertible, Sendable { throw SocketAddressError.failedToParseIPString(ipAddress) } } +#endif /// Create a new `SocketAddress` for an IP address in ByteBuffer form. /// @@ -445,6 +467,7 @@ public enum SocketAddress: CustomStringConvertible, Sendable { self = .v6(.init(address: ipv6Addr, host: "")) } +#if !os(WASI) /// Creates a new `SocketAddress` for the given host (which will be resolved) and port. /// /// - warning: This is a blocking call, so please avoid calling this from an `EventLoop`. @@ -512,6 +535,7 @@ public enum SocketAddress: CustomStringConvertible, Sendable { } #endif } +#endif } /// We define an extension on `SocketAddress` that gives it an elementwise equatable conformance, using @@ -545,9 +569,11 @@ extension SocketAddress: Equatable { return false } +#if os(WASI) + return true +#else let bufferSize = MemoryLayout.size(ofValue: addr1.address.sun_path) - // Swift implicitly binds the memory for homogeneous tuples to both the tuple type and the element type. // This allows us to use assumingMemoryBound(to:) for managing the types. However, we add a static assertion here to validate // that the element type _really is_ what we're assuming it to be. @@ -560,6 +586,7 @@ extension SocketAddress: Equatable { return strncmp(typedSunpath1, typedSunpath2, bufferSize) == 0 } } +#endif case (.v4, _), (.v6, _), (.unixDomainSocket, _): return false } @@ -578,6 +605,7 @@ extension SocketAddress: Hashable { hasher.combine(0) hasher.combine(uds.address.sun_family) +#if !os(WASI) let pathSize = MemoryLayout.size(ofValue: uds.address.sun_path) // Swift implicitly binds the memory of homogeneous tuples to both the tuple type and the element type. @@ -590,6 +618,7 @@ extension SocketAddress: Hashable { let bytes = UnsafeRawBufferPointer(start: UnsafeRawPointer(typedPathPointer), count: length) hasher.combine(bytes: bytes) } +#endif case .v4(let v4Addr): hasher.combine(1) hasher.combine(v4Addr.address.sin_family) @@ -648,6 +677,7 @@ protocol SockAddrProtocol { func withSockAddr(_ body: (UnsafePointer, Int) throws -> R) rethrows -> R } +#if !os(WASI) /// Returns a description for the given address. internal func descriptionForAddress(family: NIOBSDSocket.AddressFamily, bytes: UnsafeRawPointer, length byteCount: Int) throws -> String { var addressBytes: [Int8] = Array(repeating: 0, count: byteCount) @@ -660,6 +690,7 @@ internal func descriptionForAddress(family: NIOBSDSocket.AddressFamily, bytes: U } } } +#endif extension sockaddr_in: SockAddrProtocol { func withSockAddr(_ body: (UnsafePointer, Int) throws -> R) rethrows -> R { @@ -668,6 +699,7 @@ extension sockaddr_in: SockAddrProtocol { } } +#if !os(WASI) /// Returns a description of the `sockaddr_in`. func addressDescription() -> String { return withUnsafePointer(to: self.sin_addr) { addrPtr in @@ -675,6 +707,7 @@ extension sockaddr_in: SockAddrProtocol { try! descriptionForAddress(family: .inet, bytes: addrPtr, length: Int(INET_ADDRSTRLEN)) } } +#endif } extension sockaddr_in6: SockAddrProtocol { @@ -684,6 +717,7 @@ extension sockaddr_in6: SockAddrProtocol { } } +#if !os(WASI) /// Returns a description of the `sockaddr_in6`. func addressDescription() -> String { return withUnsafePointer(to: self.sin6_addr) { addrPtr in @@ -691,6 +725,7 @@ extension sockaddr_in6: SockAddrProtocol { try! descriptionForAddress(family: .inet6, bytes: addrPtr, length: Int(INET6_ADDRSTRLEN)) } } +#endif } extension sockaddr_un: SockAddrProtocol { @@ -709,6 +744,7 @@ extension sockaddr_storage: SockAddrProtocol { } } +#if !os(WASI) // MARK: Workarounds for SR-14268 // We need these free functions to expose our extension methods, because otherwise // the compiler falls over when we try to access them from test code. As these functions @@ -720,6 +756,7 @@ func __testOnly_addressDescription(_ addr: sockaddr_in) -> String { func __testOnly_addressDescription(_ addr: sockaddr_in6) -> String { return addr.addressDescription() } +#endif func __testOnly_withSockAddr( _ addr: sockaddr_in, _ body: (UnsafePointer, Int) throws -> ReturnType diff --git a/Sources/NIOCore/SocketOptionProvider.swift b/Sources/NIOCore/SocketOptionProvider.swift index fbb272ac0a..95e662c8e5 100644 --- a/Sources/NIOCore/SocketOptionProvider.swift +++ b/Sources/NIOCore/SocketOptionProvider.swift @@ -22,6 +22,8 @@ import Musl import CNIOLinux #elseif os(Windows) import WinSDK +#elseif canImport(WASILibc) +import WASILibc #else #error("The Socket Option provider module was unable to identify your C library.") #endif @@ -140,6 +142,7 @@ public protocol SocketOptionProvider: _NIOPreconcurrencySendable { // // You are welcome to add more helper methods here, but each helper method you add must be tested. extension SocketOptionProvider { +#if !os(WASI) /// Sets the socket option SO_LINGER to `value`. /// /// - parameters: @@ -157,6 +160,7 @@ extension SocketOptionProvider { public func getSoLinger() -> EventLoopFuture { return self.unsafeGetSocketOption(level: .socket, name: .so_linger) } +#endif /// Sets the socket option IP_MULTICAST_IF to `value`. /// diff --git a/Sources/NIOCore/SystemCallHelpers.swift b/Sources/NIOCore/SystemCallHelpers.swift index b74092a139..32ade0f05d 100644 --- a/Sources/NIOCore/SystemCallHelpers.swift +++ b/Sources/NIOCore/SystemCallHelpers.swift @@ -25,6 +25,8 @@ import Darwin.C import Glibc #elseif canImport(Musl) import Musl +#elseif canImport(WASILibc) +import WASILibc #elseif os(Windows) import CNIOWindows #else @@ -37,7 +39,9 @@ private let sysClose: @convention(c) (CInt) -> CInt = _close private let sysLseek: @convention(c) (CInt, off_t, CInt) -> off_t = _lseek private let sysRead: @convention(c) (CInt, UnsafeMutableRawPointer?, CUnsignedInt) -> CInt = _read #else +#if !os(WASI) private let sysDup: @convention(c) (CInt) -> CInt = dup +#endif private let sysClose: @convention(c) (CInt) -> CInt = close private let sysOpenWithMode: @convention(c) (UnsafePointer, CInt, NIOPOSIXFileMode) -> CInt = open private let sysLseek: @convention(c) (CInt, off_t, CInt) -> off_t = lseek @@ -47,7 +51,7 @@ private let sysRead: @convention(c) (CInt, UnsafeMutableRawPointer?, size_t) -> #if os(Android) private let sysIfNameToIndex: @convention(c) (UnsafePointer) -> CUnsignedInt = if_nametoindex private let sysGetifaddrs: @convention(c) (UnsafeMutablePointer?>) -> CInt = getifaddrs -#else +#elseif !os(WASI) private let sysIfNameToIndex: @convention(c) (UnsafePointer?) -> CUnsignedInt = if_nametoindex #if !os(Windows) private let sysGetifaddrs: @convention(c) (UnsafeMutablePointer?>?) -> CInt = getifaddrs @@ -91,8 +95,10 @@ internal func syscall(blocking: Bool, switch (err, blocking) { case (EINTR, _): continue +#if !os(WASI) case (EWOULDBLOCK, true): return .wouldBlock(0) +#endif default: preconditionIsNotUnacceptableErrno(err: err, where: function) throw IOError(errnoCode: err, reason: function) @@ -103,6 +109,7 @@ internal func syscall(blocking: Bool, } enum SystemCalls { +#if !os(WASI) @discardableResult @inline(never) internal static func dup(descriptor: CInt) throws -> CInt { @@ -110,6 +117,7 @@ enum SystemCalls { sysDup(descriptor) }.result } +#endif @inline(never) internal static func close(descriptor: CInt) throws { @@ -166,7 +174,7 @@ enum SystemCalls { sysRead(descriptor, pointer, size) } } -#else +#elseif !os(WASI) @inline(never) internal static func read(descriptor: CInt, pointer: UnsafeMutableRawPointer, size: size_t) throws -> CoreIOResult { return try syscall(blocking: true) { @@ -175,6 +183,7 @@ enum SystemCalls { } #endif +#if !os(WASI) @inline(never) internal static func if_nametoindex(_ name: UnsafePointer?) throws -> CUnsignedInt { return try syscall(blocking: false) { @@ -190,4 +199,5 @@ enum SystemCalls { } } #endif +#endif // !os(WASI) } diff --git a/Sources/NIOCore/Utilities.swift b/Sources/NIOCore/Utilities.swift index f00ae18ecc..effb617c1c 100644 --- a/Sources/NIOCore/Utilities.swift +++ b/Sources/NIOCore/Utilities.swift @@ -36,6 +36,8 @@ import struct WinSDK.ULONG import typealias WinSDK.DWORD #elseif canImport(Darwin) import Darwin +#elseif canImport(WASILibc) +import WASILibc #else #error("The Core utilities module was unable to identify your C library.") #endif @@ -116,7 +118,7 @@ public enum System { #endif } -#if !os(Windows) +#if !os(Windows) && !os(WASI) /// A utility function that enumerates the available network interfaces on this machine. /// /// This function returns values that are true for a brief snapshot in time. These results can @@ -193,7 +195,7 @@ public enum System { } pAdapter = pAdapter!.pointee.Next } -#else +#elseif !os(WASI) var interface: UnsafeMutablePointer? = nil try SystemCalls.getifaddrs(&interface) let originalInterface = interface diff --git a/Sources/NIOEmbedded/AsyncTestingEventLoop.swift b/Sources/NIOEmbedded/AsyncTestingEventLoop.swift index bdc9423c85..e795650a15 100644 --- a/Sources/NIOEmbedded/AsyncTestingEventLoop.swift +++ b/Sources/NIOEmbedded/AsyncTestingEventLoop.swift @@ -13,7 +13,9 @@ //===----------------------------------------------------------------------===// import Atomics +#if canImport(Dispatch) import Dispatch +#endif import _NIODataStructures import NIOCore import NIOConcurrencyHelpers diff --git a/Sources/NIOEmbedded/Embedded.swift b/Sources/NIOEmbedded/Embedded.swift index 4a8ce0d218..fc3e6358ff 100644 --- a/Sources/NIOEmbedded/Embedded.swift +++ b/Sources/NIOEmbedded/Embedded.swift @@ -14,7 +14,9 @@ import Atomics import NIOConcurrencyHelpers +#if canImport(Dispatch) import Dispatch +#endif import _NIODataStructures import NIOCore import DequeModule diff --git a/Sources/NIOPosix/GetaddrinfoResolver.swift b/Sources/NIOPosix/GetaddrinfoResolver.swift index 633e91d0f2..ef4b0a15da 100644 --- a/Sources/NIOPosix/GetaddrinfoResolver.swift +++ b/Sources/NIOPosix/GetaddrinfoResolver.swift @@ -24,7 +24,9 @@ import NIOCore /// /// This resolver is a single-use object: it can only be used to perform a single host resolution. +#if canImport(Dispatch) import Dispatch +#endif #if os(Linux) || os(FreeBSD) || os(Android) import CNIOLinux diff --git a/Sources/NIOPosix/MultiThreadedEventLoopGroup.swift b/Sources/NIOPosix/MultiThreadedEventLoopGroup.swift index aaa685f220..fe9ea8a03d 100644 --- a/Sources/NIOPosix/MultiThreadedEventLoopGroup.swift +++ b/Sources/NIOPosix/MultiThreadedEventLoopGroup.swift @@ -14,7 +14,9 @@ import NIOCore import NIOConcurrencyHelpers +#if canImport(Dispatch) import Dispatch +#endif import Atomics struct NIORegistration: Registration { diff --git a/Sources/NIOPosix/NIOThreadPool.swift b/Sources/NIOPosix/NIOThreadPool.swift index ed63e10e63..73dc5b65d1 100644 --- a/Sources/NIOPosix/NIOThreadPool.swift +++ b/Sources/NIOPosix/NIOThreadPool.swift @@ -13,7 +13,9 @@ //===----------------------------------------------------------------------===// import DequeModule +#if canImport(Dispatch) import Dispatch +#endif import NIOConcurrencyHelpers import NIOCore diff --git a/Sources/_NIODataStructures/Heap.swift b/Sources/_NIODataStructures/Heap.swift index 15a2a32b09..c68ba8e507 100644 --- a/Sources/_NIODataStructures/Heap.swift +++ b/Sources/_NIODataStructures/Heap.swift @@ -17,6 +17,8 @@ import Darwin.C import Glibc #elseif canImport(Musl) import Musl +#elseif canImport(WASILibc) +import WASILibc #elseif os(Windows) import ucrt #else From 66e3f0e2958a2700fb3c318e1b0ecf66454b8618 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 4 Mar 2024 14:16:12 +0000 Subject: [PATCH 02/10] Clean up formatting --- Sources/CNIOWASI/include/CNIOWASI.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CNIOWASI/include/CNIOWASI.h b/Sources/CNIOWASI/include/CNIOWASI.h index 2582e3f8f3..80fd247f33 100644 --- a/Sources/CNIOWASI/include/CNIOWASI.h +++ b/Sources/CNIOWASI/include/CNIOWASI.h @@ -9,4 +9,4 @@ static inline void CNIOWASI_gettime(struct timespec *tv) { clock_gettime(CLOCK_MONOTONIC, tv); } -#endif \ No newline at end of file +#endif From 917071257d3f3fc36861d636c2684d2047064b69 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 4 Mar 2024 14:17:25 +0000 Subject: [PATCH 03/10] Clean up formatting --- Sources/NIOConcurrencyHelpers/lock.swift | 2 +- Sources/NIOCore/SocketAddresses.swift | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/NIOConcurrencyHelpers/lock.swift b/Sources/NIOConcurrencyHelpers/lock.swift index 1fa61d381b..da7f089287 100644 --- a/Sources/NIOConcurrencyHelpers/lock.swift +++ b/Sources/NIOConcurrencyHelpers/lock.swift @@ -252,7 +252,7 @@ public final class ConditionLock { let allNSecs: Int64 = timeoutNS + Int64(curTime.tv_usec) * 1000 var timeoutAbs = timespec(tv_sec: curTime.tv_sec + Int((allNSecs / nsecPerSec)), - tv_nsec: Int(allNSecs % nsecPerSec)) + tv_nsec: Int(allNSecs % nsecPerSec)) assert(timeoutAbs.tv_nsec >= 0 && timeoutAbs.tv_nsec < Int(nsecPerSec)) assert(timeoutAbs.tv_sec >= curTime.tv_sec) return self.mutex.withLockPrimitive { mutex -> Bool in diff --git a/Sources/NIOCore/SocketAddresses.swift b/Sources/NIOCore/SocketAddresses.swift index d0a77dcaa3..cc6f9e3c6a 100644 --- a/Sources/NIOCore/SocketAddresses.swift +++ b/Sources/NIOCore/SocketAddresses.swift @@ -574,6 +574,7 @@ extension SocketAddress: Equatable { #else let bufferSize = MemoryLayout.size(ofValue: addr1.address.sun_path) + // Swift implicitly binds the memory for homogeneous tuples to both the tuple type and the element type. // This allows us to use assumingMemoryBound(to:) for managing the types. However, we add a static assertion here to validate // that the element type _really is_ what we're assuming it to be. From 1958d9149722c588cdfc360eab6d79874ccca006 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 4 Mar 2024 14:52:58 +0000 Subject: [PATCH 04/10] Fix `NIOEmbedded` for WASI --- Sources/NIOEmbedded/AsyncTestingChannel.swift | 2 ++ Sources/NIOEmbedded/AsyncTestingEventLoop.swift | 4 ++-- Sources/NIOEmbedded/Embedded.swift | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Sources/NIOEmbedded/AsyncTestingChannel.swift b/Sources/NIOEmbedded/AsyncTestingChannel.swift index 3324db25d7..c8609e81b9 100644 --- a/Sources/NIOEmbedded/AsyncTestingChannel.swift +++ b/Sources/NIOEmbedded/AsyncTestingChannel.swift @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#if canImport(Dispatch) import NIOConcurrencyHelpers import NIOCore @@ -649,3 +650,4 @@ extension NIOAsyncTestingChannel.LeftOverState: @unchecked Sendable { } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension NIOAsyncTestingChannel.BufferState: @unchecked Sendable { } +#endif diff --git a/Sources/NIOEmbedded/AsyncTestingEventLoop.swift b/Sources/NIOEmbedded/AsyncTestingEventLoop.swift index e795650a15..f324e18321 100644 --- a/Sources/NIOEmbedded/AsyncTestingEventLoop.swift +++ b/Sources/NIOEmbedded/AsyncTestingEventLoop.swift @@ -12,10 +12,9 @@ // //===----------------------------------------------------------------------===// -import Atomics #if canImport(Dispatch) +import Atomics import Dispatch -#endif import _NIODataStructures import NIOCore import NIOConcurrencyHelpers @@ -383,3 +382,4 @@ private class PromiseCreationStore { precondition(self.promiseCreationStore.isEmpty, "NIOAsyncTestingEventLoop freed with uncompleted promises!") } } +#endif diff --git a/Sources/NIOEmbedded/Embedded.swift b/Sources/NIOEmbedded/Embedded.swift index fc3e6358ff..c96fa6198d 100644 --- a/Sources/NIOEmbedded/Embedded.swift +++ b/Sources/NIOEmbedded/Embedded.swift @@ -203,6 +203,7 @@ public final class EmbeddedEventLoop: EventLoop { // Nothing to do here } +#if canImport(Dispatch) /// - see: `EventLoop.shutdownGracefully` public func shutdownGracefully(queue: DispatchQueue, _ callback: @escaping (Error?) -> Void) { run() @@ -210,6 +211,7 @@ public final class EmbeddedEventLoop: EventLoop { callback(nil) } } +#endif public func _preconditionSafeToWait(file: StaticString, line: UInt) { // EmbeddedEventLoop always allows a wait, as waiting will essentially always block From 4cfee579c621e8cf6b08a7dc18f7a737a71417d8 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 4 Mar 2024 15:01:11 +0000 Subject: [PATCH 05/10] `NIOHTTP1` should depend on `NIOPosix` only on Posix-ish platforms --- Package.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Package.swift b/Package.swift index f31946cfbf..0ae7afce6f 100644 --- a/Package.swift +++ b/Package.swift @@ -15,15 +15,16 @@ import PackageDescription +let posixishPlatforms: [Platform] = [.macOS, .iOS, .tvOS, .watchOS, .linux, .android] + let swiftAtomics: PackageDescription.Target.Dependency = .product(name: "Atomics", package: "swift-atomics") let swiftCollections: PackageDescription.Target.Dependency = .product(name: "DequeModule", package: "swift-collections") let swiftSystem: PackageDescription.Target.Dependency = .product( name: "SystemPackage", package: "swift-system", - condition: .when(platforms: [.macOS, .iOS, .tvOS, .watchOS, .linux, .android]) + condition: .when(platforms: posixishPlatforms) ) - let package = Package( name: "swift-nio", products: [ @@ -156,7 +157,7 @@ let package = Package( .target( name: "NIOHTTP1", dependencies: [ - "NIO", + .target(name: "NIO", condition: .when(platforms: posixishPlatforms)), "NIOCore", "NIOConcurrencyHelpers", "CNIOLLHTTP", From e5ccda57f84596c376337974edf57c875344b358 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 4 Mar 2024 15:07:07 +0000 Subject: [PATCH 06/10] Clean up naming in `Package.swift` --- Package.swift | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Package.swift b/Package.swift index 0ae7afce6f..a1162da2b7 100644 --- a/Package.swift +++ b/Package.swift @@ -15,16 +15,19 @@ import PackageDescription -let posixishPlatforms: [Platform] = [.macOS, .iOS, .tvOS, .watchOS, .linux, .android] let swiftAtomics: PackageDescription.Target.Dependency = .product(name: "Atomics", package: "swift-atomics") let swiftCollections: PackageDescription.Target.Dependency = .product(name: "DequeModule", package: "swift-collections") let swiftSystem: PackageDescription.Target.Dependency = .product( name: "SystemPackage", package: "swift-system", - condition: .when(platforms: posixishPlatforms) + condition: .when(platforms: [.macOS, .iOS, .tvOS, .watchOS, .linux, .android]) ) +// These platforms require a depdency on `NIOPosix` from `NIOHTTP1` to maintain backward +// compatibility with previous NIO versions. +let historicalNIOPosixDependencyRequired: [Platform] = [.macOS, .iOS, .tvOS, .watchOS, .linux, .android] + let package = Package( name: "swift-nio", products: [ @@ -157,7 +160,7 @@ let package = Package( .target( name: "NIOHTTP1", dependencies: [ - .target(name: "NIO", condition: .when(platforms: posixishPlatforms)), + .target(name: "NIO", condition: .when(platforms: historicalNIOPosixDependencyRequired)), "NIOCore", "NIOConcurrencyHelpers", "CNIOLLHTTP", From 9fe470afe71f8675141dbc7647c47e82bd4b8d09 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 4 Mar 2024 16:19:01 +0000 Subject: [PATCH 07/10] Bump to later commit of swift-atomics --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index a1162da2b7..fb9e1e1b59 100644 --- a/Package.swift +++ b/Package.swift @@ -46,7 +46,7 @@ let package = Package( .library(name: "_NIOFileSystemFoundationCompat", targets: ["NIOFileSystemFoundationCompat"]), ], dependencies: [ - .package(url: "https://github.com/apple/swift-atomics.git", from: "1.1.0"), + .package(url: "https://github.com/apple/swift-atomics.git", revision: "1f2007ef4165432f59f28615c473eb79a844a2af"), .package(url: "https://github.com/apple/swift-collections.git", from: "1.0.2"), .package(url: "https://github.com/apple/swift-system.git", from: "1.2.0"), .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), From 85f140e74afab81ad08fb377c18e924c979c3882 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 8 Mar 2024 15:33:09 +0000 Subject: [PATCH 08/10] Address PR feedback --- Sources/CNIOWASI/include/CNIOWASI.h | 6 ++++++ Sources/NIOCore/BSDSocketAPI.swift | 11 ++++------ Sources/NIOCore/FileHandle.swift | 12 ++++++++--- Sources/NIOCore/Interfaces.swift | 11 +++++----- Sources/NIOCore/SocketAddresses.swift | 29 +++++---------------------- 5 files changed, 30 insertions(+), 39 deletions(-) diff --git a/Sources/CNIOWASI/include/CNIOWASI.h b/Sources/CNIOWASI/include/CNIOWASI.h index 80fd247f33..787d4154eb 100644 --- a/Sources/CNIOWASI/include/CNIOWASI.h +++ b/Sources/CNIOWASI/include/CNIOWASI.h @@ -2,6 +2,7 @@ #if __wasi__ +#include #include static inline void CNIOWASI_gettime(struct timespec *tv) { @@ -9,4 +10,9 @@ static inline void CNIOWASI_gettime(struct timespec *tv) { clock_gettime(CLOCK_MONOTONIC, tv); } +static inline int CNIOWASI_O_CREAT() { + // ClangImporter doesn't support `O_CREATE` declaration in WASILibc, thus we have to define a bridge manually + return O_CREAT; +} + #endif diff --git a/Sources/NIOCore/BSDSocketAPI.swift b/Sources/NIOCore/BSDSocketAPI.swift index a42400815f..4026fbaf76 100644 --- a/Sources/NIOCore/BSDSocketAPI.swift +++ b/Sources/NIOCore/BSDSocketAPI.swift @@ -82,6 +82,9 @@ private let sysInet_ntop: @convention(c) (CInt, UnsafeRawPointer?, UnsafeMutable private let sysInet_pton: @convention(c) (CInt, UnsafePointer?, UnsafeMutableRawPointer?) -> CInt = inet_pton #elseif canImport(WASILibc) import WASILibc + +private let sysInet_ntop: @convention(c) (CInt, UnsafeRawPointer?, UnsafeMutablePointer?, socklen_t) -> UnsafePointer? = inet_ntop +private let sysInet_pton: @convention(c) (CInt, UnsafePointer?, UnsafeMutableRawPointer?) -> CInt = inet_pton #else #error("The BSD Socket module was unable to identify your C library.") #endif @@ -193,11 +196,6 @@ extension NIOBSDSocket.AddressFamily { // Protocol Family extension NIOBSDSocket.ProtocolFamily { -#if os(WASI) - /// UNIX local to the host. - public static let unix: NIOBSDSocket.ProtocolFamily = - NIOBSDSocket.ProtocolFamily(rawValue: 1) -#else /// IP network 4 protocol. public static let inet: NIOBSDSocket.ProtocolFamily = NIOBSDSocket.ProtocolFamily(rawValue: PF_INET) @@ -206,6 +204,7 @@ extension NIOBSDSocket.ProtocolFamily { public static let inet6: NIOBSDSocket.ProtocolFamily = NIOBSDSocket.ProtocolFamily(rawValue: PF_INET6) +#if !os(WASI) /// UNIX local to the host. public static let unix: NIOBSDSocket.ProtocolFamily = NIOBSDSocket.ProtocolFamily(rawValue: PF_UNIX) @@ -415,7 +414,6 @@ extension NIOBSDSocket.Option { } #endif -#if !os(WASI) extension NIOBSDSocket { // Sadly this was defined on BSDSocket, and we need it for SocketAddress. @inline(never) @@ -455,4 +453,3 @@ extension NIOBSDSocket { #endif } } -#endif diff --git a/Sources/NIOCore/FileHandle.swift b/Sources/NIOCore/FileHandle.swift index b063f3c266..a71bcd602b 100644 --- a/Sources/NIOCore/FileHandle.swift +++ b/Sources/NIOCore/FileHandle.swift @@ -21,6 +21,7 @@ import Glibc import Musl #elseif canImport(WASILibc) import WASILibc +import CNIOWASI #else #error("The File Handle module was unable to identify your C library.") #endif @@ -138,19 +139,24 @@ extension NIOFileHandle { #if os(Windows) public static let defaultPermissions = _S_IREAD | _S_IWRITE +#elseif os(WASI) + public static let defaultPermissions = WASILibc.S_IWUSR | WASILibc.S_IRUSR | WASILibc.S_IRGRP | WASILibc.S_IROTH #else public static let defaultPermissions = S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH #endif -#if !os(WASI) /// Allows file creation when opening file for writing. File owner is set to the effective user ID of the process. /// /// - parameters: /// - posixMode: `file mode` applied when file is created. Default permissions are: read and write for fileowner, read for owners group and others. public static func allowFileCreation(posixMode: NIOPOSIXFileMode = defaultPermissions) -> Flags { - return Flags(posixMode: posixMode, posixFlags: O_CREAT) + #if os(WASI) + let flags = CNIOWASI_O_CREAT() + #else + let flags = O_CREAT + #endif + return Flags(posixMode: posixMode, posixFlags: flags) } -#endif /// Allows the specification of POSIX flags (e.g. `O_TRUNC`) and mode (e.g. `S_IWUSR`) /// diff --git a/Sources/NIOCore/Interfaces.swift b/Sources/NIOCore/Interfaces.swift index 2c7c7d3e1d..db5f220f1a 100644 --- a/Sources/NIOCore/Interfaces.swift +++ b/Sources/NIOCore/Interfaces.swift @@ -64,7 +64,6 @@ private extension ifaddrs { } #endif -#if !os(WASI) /// A representation of a single network interface on a system. @available(*, deprecated, renamed: "NIONetworkDevice") public final class NIONetworkInterface: Sendable { @@ -96,6 +95,11 @@ public final class NIONetworkInterface: Sendable { /// The index of the interface, as provided by `if_nametoindex`. public let interfaceIndex: Int +#if os(WASI) + @available(*, unavailable) + init() { fatalError() } +#endif + #if os(Windows) internal init?(_ pAdapter: UnsafeMutablePointer, _ pAddress: UnsafeMutablePointer) { @@ -124,7 +128,7 @@ public final class NIONetworkInterface: Sendable { self.pointToPointDestinationAddress = nil self.multicastSupported = false } -#else +#elseif !os(WASI) internal init?(_ caddr: ifaddrs) { self.name = String(cString: caddr.ifa_name!) @@ -189,7 +193,6 @@ extension NIONetworkInterface: Equatable { lhs.interfaceIndex == rhs.interfaceIndex } } -#endif /// A helper extension for working with sockaddr pointers. extension UnsafeMutablePointer where Pointee == sockaddr { @@ -197,12 +200,10 @@ extension UnsafeMutablePointer where Pointee == sockaddr { fileprivate func convert() -> SocketAddress? { let addressBytes = UnsafeRawPointer(self) switch NIOBSDSocket.AddressFamily(rawValue: CInt(pointee.sa_family)) { -#if !os(WASI) case .inet: return SocketAddress(addressBytes.load(as: sockaddr_in.self)) case .inet6: return SocketAddress(addressBytes.load(as: sockaddr_in6.self)) -#endif case .unix: return SocketAddress(addressBytes.load(as: sockaddr_un.self)) default: diff --git a/Sources/NIOCore/SocketAddresses.swift b/Sources/NIOCore/SocketAddresses.swift index cc6f9e3c6a..2a4d0ab445 100644 --- a/Sources/NIOCore/SocketAddresses.swift +++ b/Sources/NIOCore/SocketAddresses.swift @@ -135,9 +135,6 @@ public enum SocketAddress: CustomStringConvertible, Sendable { /// A human-readable description of this `SocketAddress`. Mostly useful for logging. public var description: String { -#if os(WASI) - return "[UDS]" -#else let addressString: String let port: String let host: String? @@ -166,7 +163,6 @@ public enum SocketAddress: CustomStringConvertible, Sendable { } return "[\(type)]\(host.map { "\($0)/\(addressString):" } ?? "\(addressString):")\(port)" -#endif } @available(*, deprecated, renamed: "SocketAddress.protocol") @@ -176,21 +172,20 @@ public enum SocketAddress: CustomStringConvertible, Sendable { /// Returns the protocol family as defined in `man 2 socket` of this `SocketAddress`. public var `protocol`: NIOBSDSocket.ProtocolFamily { -#if os(WASI) - return .unix -#else switch self { case .v4: return .inet case .v6: return .inet6 case .unixDomainSocket: + #if os(WASI) + fatalError("unix domain sockets are currently not supported by WASILibc") + #else return .unix + #endif } -#endif } -#if !os(WASI) /// Get the IP address as a string public var ipAddress: String? { switch self { @@ -206,8 +201,6 @@ public enum SocketAddress: CustomStringConvertible, Sendable { return nil } } -#endif - /// Get and set the port associated with the address, if defined. /// When setting to `nil` the port will default to `0` for compatible sockets. The rationale for this is that both `nil` and `0` can /// be interpreted as "no preference". @@ -295,7 +288,6 @@ public enum SocketAddress: CustomStringConvertible, Sendable { self = .v6(.init(address: addr, host: host)) } -#if !os(WASI) /// Creates a new IPv4 `SocketAddress`. /// /// - parameters: @@ -311,7 +303,6 @@ public enum SocketAddress: CustomStringConvertible, Sendable { public init(_ addr: sockaddr_in6) { self = .v6(.init(address: addr, host: addr.addressDescription())) } -#endif /// Creates a new Unix Domain Socket `SocketAddress`. /// @@ -348,7 +339,6 @@ public enum SocketAddress: CustomStringConvertible, Sendable { self = .unixDomainSocket(.init(address: addr)) } -#if !os(WASI) /// Create a new `SocketAddress` for an IP address in string form. /// /// - parameters: @@ -392,8 +382,7 @@ public enum SocketAddress: CustomStringConvertible, Sendable { throw SocketAddressError.failedToParseIPString(ipAddress) } } -#endif - + /// Create a new `SocketAddress` for an IP address in ByteBuffer form. /// /// - parameters: @@ -678,7 +667,6 @@ protocol SockAddrProtocol { func withSockAddr(_ body: (UnsafePointer, Int) throws -> R) rethrows -> R } -#if !os(WASI) /// Returns a description for the given address. internal func descriptionForAddress(family: NIOBSDSocket.AddressFamily, bytes: UnsafeRawPointer, length byteCount: Int) throws -> String { var addressBytes: [Int8] = Array(repeating: 0, count: byteCount) @@ -691,7 +679,6 @@ internal func descriptionForAddress(family: NIOBSDSocket.AddressFamily, bytes: U } } } -#endif extension sockaddr_in: SockAddrProtocol { func withSockAddr(_ body: (UnsafePointer, Int) throws -> R) rethrows -> R { @@ -700,7 +687,6 @@ extension sockaddr_in: SockAddrProtocol { } } -#if !os(WASI) /// Returns a description of the `sockaddr_in`. func addressDescription() -> String { return withUnsafePointer(to: self.sin_addr) { addrPtr in @@ -708,7 +694,6 @@ extension sockaddr_in: SockAddrProtocol { try! descriptionForAddress(family: .inet, bytes: addrPtr, length: Int(INET_ADDRSTRLEN)) } } -#endif } extension sockaddr_in6: SockAddrProtocol { @@ -718,7 +703,6 @@ extension sockaddr_in6: SockAddrProtocol { } } -#if !os(WASI) /// Returns a description of the `sockaddr_in6`. func addressDescription() -> String { return withUnsafePointer(to: self.sin6_addr) { addrPtr in @@ -726,7 +710,6 @@ extension sockaddr_in6: SockAddrProtocol { try! descriptionForAddress(family: .inet6, bytes: addrPtr, length: Int(INET6_ADDRSTRLEN)) } } -#endif } extension sockaddr_un: SockAddrProtocol { @@ -745,7 +728,6 @@ extension sockaddr_storage: SockAddrProtocol { } } -#if !os(WASI) // MARK: Workarounds for SR-14268 // We need these free functions to expose our extension methods, because otherwise // the compiler falls over when we try to access them from test code. As these functions @@ -757,7 +739,6 @@ func __testOnly_addressDescription(_ addr: sockaddr_in) -> String { func __testOnly_addressDescription(_ addr: sockaddr_in6) -> String { return addr.addressDescription() } -#endif func __testOnly_withSockAddr( _ addr: sockaddr_in, _ body: (UnsafePointer, Int) throws -> ReturnType From 4ee91c6595ad4b5b5b0bb205a49fca788e3d1619 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 8 Mar 2024 15:40:18 +0000 Subject: [PATCH 09/10] Reduce the diff --- Package.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index fb9e1e1b59..692b9ea646 100644 --- a/Package.swift +++ b/Package.swift @@ -15,7 +15,6 @@ import PackageDescription - let swiftAtomics: PackageDescription.Target.Dependency = .product(name: "Atomics", package: "swift-atomics") let swiftCollections: PackageDescription.Target.Dependency = .product(name: "DequeModule", package: "swift-collections") let swiftSystem: PackageDescription.Target.Dependency = .product( @@ -46,7 +45,7 @@ let package = Package( .library(name: "_NIOFileSystemFoundationCompat", targets: ["NIOFileSystemFoundationCompat"]), ], dependencies: [ - .package(url: "https://github.com/apple/swift-atomics.git", revision: "1f2007ef4165432f59f28615c473eb79a844a2af"), + .package(url: "https://github.com/apple/swift-atomics.git", from: "1.1.0"), .package(url: "https://github.com/apple/swift-collections.git", from: "1.0.2"), .package(url: "https://github.com/apple/swift-system.git", from: "1.2.0"), .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), From b1b5d7fe2aa38a0e53968031de8339e53978fde3 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 8 Mar 2024 15:48:03 +0000 Subject: [PATCH 10/10] spaces >> tabs --- Sources/CNIOWASI/include/CNIOWASI.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/CNIOWASI/include/CNIOWASI.h b/Sources/CNIOWASI/include/CNIOWASI.h index 787d4154eb..826eaac824 100644 --- a/Sources/CNIOWASI/include/CNIOWASI.h +++ b/Sources/CNIOWASI/include/CNIOWASI.h @@ -7,12 +7,12 @@ static inline void CNIOWASI_gettime(struct timespec *tv) { // ClangImporter doesn't support `CLOCK_MONOTONIC` declaration in WASILibc, thus we have to define a bridge manually - clock_gettime(CLOCK_MONOTONIC, tv); + clock_gettime(CLOCK_MONOTONIC, tv); } static inline int CNIOWASI_O_CREAT() { // ClangImporter doesn't support `O_CREATE` declaration in WASILibc, thus we have to define a bridge manually - return O_CREAT; + return O_CREAT; } #endif