diff --git a/Sources/System/FileOperations.swift b/Sources/System/FileOperations.swift index e2332eee..1193a04a 100644 --- a/Sources/System/FileOperations.swift +++ b/Sources/System/FileOperations.swift @@ -370,4 +370,27 @@ extension FileDescriptor { public func dup2() throws -> FileDescriptor { fatalError("Not implemented") } + + #if !os(Windows) + /// Create a pipe, a unidirectional data channel which can be used for interprocess communication. + /// + /// - Returns: The pair of file descriptors. + /// + /// The corresponding C function is `pipe`. + @_alwaysEmitIntoClient + // @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) + public static func pipe() throws -> (readEnd: FileDescriptor, writeEnd: FileDescriptor) { + try _pipe().get() + } + + @usableFromInline + internal static func _pipe() -> Result<(readEnd: FileDescriptor, writeEnd: FileDescriptor), Errno> { + var fds: (Int32, Int32) = (-1, -1) + return withUnsafeMutablePointer(to: &fds) { pointer in + valueOrErrno(retryOnInterrupt: false) { + system_pipe(UnsafeMutableRawPointer(pointer).assumingMemoryBound(to: Int32.self)) + } + }.map { _ in (.init(rawValue: fds.0), .init(rawValue: fds.1)) } + } + #endif } diff --git a/Sources/System/Internals/Syscalls.swift b/Sources/System/Internals/Syscalls.swift index ecfdc843..453c02fc 100644 --- a/Sources/System/Internals/Syscalls.swift +++ b/Sources/System/Internals/Syscalls.swift @@ -115,3 +115,11 @@ internal func system_dup2(_ fd: Int32, _ fd2: Int32) -> Int32 { #endif return dup2(fd, fd2) } +#if !os(Windows) +internal func system_pipe(_ fds: UnsafeMutablePointer) -> CInt { +#if ENABLE_MOCKING + if mockingEnabled { return _mock(fds) } +#endif + return pipe(fds) +} +#endif diff --git a/Tests/SystemTests/FileOperationsTest.swift b/Tests/SystemTests/FileOperationsTest.swift index f39a052a..a65301df 100644 --- a/Tests/SystemTests/FileOperationsTest.swift +++ b/Tests/SystemTests/FileOperationsTest.swift @@ -89,6 +89,27 @@ final class FileOperationsTest: XCTestCase { func testHelpers() { // TODO: Test writeAll, writeAll(toAbsoluteOffset), closeAfter } + +#if !os(Windows) + func testAdHocPipe() throws { + // Ad-hoc test testing `Pipe` functionality. + // We cannot test `Pipe` using `MockTestCase` because it calls `pipe` with a pointer to an array local to the `Pipe`, the address of which we do not know prior to invoking `Pipe`. + let pipe = try FileDescriptor.pipe() + try pipe.readEnd.closeAfter { + try pipe.writeEnd.closeAfter { + var abc = "abc" + try abc.withUTF8 { + _ = try pipe.writeEnd.write(UnsafeRawBufferPointer($0)) + } + let readLen = 3 + let readBytes = try Array(unsafeUninitializedCapacity: readLen) { buf, count in + count = try pipe.readEnd.read(into: UnsafeMutableRawBufferPointer(buf)) + } + XCTAssertEqual(readBytes, Array(abc.utf8)) + } + } + } +#endif func testAdHocOpen() { // Ad-hoc test touching a file system.