From de2b68cf203d4304753ec49e8050ac968b16f43e Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 27 Mar 2025 21:27:55 -0700 Subject: [PATCH] Add a function on `LocalFileOutputByteStream` to disable `SIGPIPE` when writing data to it after the receiving end is closed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this, if we launch a subprocess and try to write data to its stdin after it has finished (or crashed), we would take down the current process due to the SIGPIPE, which is usually not desirable. Eg. SourceKit-LSP doesn’t want to exit with a SIGPIPE exit code if a launched swift-format subprocesses crashes before the file to format could transferred to its stdin. This mirrors behavior of `DispatchIO`, which also doesn’t emit a `SIGPIPE` when trying to write data to a stream whose receiving end has closed. --- Sources/TSCBasic/WritableByteStream.swift | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Sources/TSCBasic/WritableByteStream.swift b/Sources/TSCBasic/WritableByteStream.swift index 7c95b564..a68ff8d0 100644 --- a/Sources/TSCBasic/WritableByteStream.swift +++ b/Sources/TSCBasic/WritableByteStream.swift @@ -817,6 +817,25 @@ public final class LocalFileOutputByteStream: FileOutputByteStream { throw error } } + + #if canImport(Darwin) + /// Disable the SIGPIPE if data is written to this stream after its receiving end has been terminated. + /// + /// This can be useful to stop the current process from crashing if it tries to write data to the stdin stream of a + /// subprocess after it has finished or crashed. + /// + /// Only available on Darwin because `F_SETNOSIGPIPE` is not universally available. + public func disableSigpipe() throws { + let fileDescriptor = fileno(filePointer) + if fileDescriptor == -1 { + throw FileSystemError(.ioError(code: errno)) + } + let fcntlResult = fcntl(fileDescriptor, F_SETNOSIGPIPE, 1) + if fcntlResult == -1 { + throw FileSystemError(.ioError(code: errno)) + } + } + #endif } /// Public stdout stream instance.