Skip to content

Commit ee53f85

Browse files
committed
Globally ignore SIGPIPE on Linux
We receive a `SIGPIPE` if we write to a pipe that points to a crashed process. This in particular happens if the target of a `JSONRPCConnection` has crashed and we try to send it a message. On Darwin, `DispatchIO` ignores `SIGPIPE` for the pipes handled by it, but that features is not available on Linux. Instead, globally ignore `SIGPIPE` on Linux to prevent us from crashing if the `JSONRPCConnection`'s target crashes. Fixes rdar://75580936
1 parent bf31e76 commit ee53f85

File tree

4 files changed

+40
-3
lines changed

4 files changed

+40
-3
lines changed

Sources/LanguageServerProtocolJSONRPC/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
add_library(LanguageServerProtocolJSONRPC
2+
DisableSigpipe.swift
23
JSONRPCConnection.swift
34
MessageCoding.swift
45
MessageSplitting.swift)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
14+
#if canImport(Glibc)
15+
import Glibc
16+
#endif
17+
18+
#if os(Linux) || os(Android)
19+
// This is a lazily initialised global variable that when read for the first time, will ignore SIGPIPE.
20+
private let globallyIgnoredSIGPIPE: Bool = {
21+
/* no F_SETNOSIGPIPE on Linux :( */
22+
_ = Glibc.signal(SIGPIPE, SIG_IGN)
23+
return true
24+
}()
25+
26+
internal func globallyDisableSigpipe() {
27+
let haveWeIgnoredSIGPIEThisIsHereToTriggerIgnoringIt = globallyIgnoredSIGPIPE
28+
guard haveWeIgnoredSIGPIEThisIsHereToTriggerIgnoringIt else {
29+
fatalError("globallyIgnoredSIGPIPE should always be true")
30+
}
31+
}
32+
33+
#endif

Sources/LanguageServerProtocolJSONRPC/JSONRPCConnection.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ public final class JSONRPCConnection {
6363
outFD: FileHandle,
6464
syncRequests: Bool = false)
6565
{
66+
#if os(Linux) || os(Android)
67+
// We receive a `SIGPIPE` if we write to a pipe that points to a crashed process. This in particular happens if the target of a `JSONRPCConnection` has crashed and we try to send it a message.
68+
// On Darwin, `DispatchIO` ignores `SIGPIPE` for the pipes handled by it, but that features is not available on Linux.
69+
// Instead, globally ignore `SIGPIPE` on Linux to prevent us from crashing if the `JSONRPCConnection`'s target crashes.
70+
globallyDisableSigpipe()
71+
#endif
6672
state = .created
6773
self.messageRegistry = messageRegistry
6874
self.syncRequests = syncRequests

Tests/SourceKitDTests/CrashRecoveryTests.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ final class CrashRecoveryTests: XCTestCase {
152152
}
153153

154154
func testClangdCrashRecovery() throws {
155-
try XCTSkipUnless(isDarwinHost, "rdar://75580936 failing on Linux in CI sometimes")
156155
try XCTSkipUnless(longTestsEnabled)
157156

158157
let ws = try! staticSourceKitTibsWorkspace(name: "ClangCrashRecovery")!
@@ -190,7 +189,6 @@ final class CrashRecoveryTests: XCTestCase {
190189
}
191190

192191
func testClangdCrashRecoveryReopensWithCorrectBuildSettings() throws {
193-
try XCTSkipUnless(isDarwinHost, "rdar://75580936 failing on Linux in CI sometimes")
194192
try XCTSkipUnless(longTestsEnabled)
195193

196194
let ws = try! staticSourceKitTibsWorkspace(name: "ClangCrashRecoveryBuildSettings")!
@@ -224,7 +222,6 @@ final class CrashRecoveryTests: XCTestCase {
224222
}
225223

226224
func testPreventClangdCrashLoop() throws {
227-
try XCTSkipUnless(isDarwinHost, "rdar://75580936 failing on Linux in CI sometimes")
228225
try XCTSkipUnless(longTestsEnabled)
229226

230227
let ws = try! staticSourceKitTibsWorkspace(name: "ClangCrashRecovery")!

0 commit comments

Comments
 (0)