@@ -100,12 +100,8 @@ actor ClangLanguageService: LanguageService, MessageHandler {
100
100
/// Type to map `clangd`'s semantic token legend to SourceKit-LSP's.
101
101
private var semanticTokensTranslator : SemanticTokensLegendTranslator ? = nil
102
102
103
- /// While `clangd` is running, its PID.
104
- #if os(Windows)
105
- private var hClangd : HANDLE = INVALID_HANDLE_VALUE
106
- #else
107
- private var clangdPid : Int32 ?
108
- #endif
103
+ /// While `clangd` is running, its `Process` object.
104
+ private var clangdProcess : Process ?
109
105
110
106
/// Creates a language server for the given client referencing the clang binary specified in `toolchain`.
111
107
/// Returns `nil` if `clangd` can't be found.
@@ -159,11 +155,7 @@ actor ClangLanguageService: LanguageService, MessageHandler {
159
155
///
160
156
/// - Parameter terminationStatus: The exit code of `clangd`.
161
157
private func handleClangdTermination( terminationStatus: Int32 ) {
162
- #if os(Windows)
163
- self . hClangd = INVALID_HANDLE_VALUE
164
- #else
165
- self . clangdPid = nil
166
- #endif
158
+ self . clangdProcess = nil
167
159
if terminationStatus != 0 {
168
160
self . state = . connectionInterrupted
169
161
self . restartClangd ( )
@@ -196,11 +188,7 @@ actor ClangLanguageService: LanguageService, MessageHandler {
196
188
)
197
189
self . clangd = connectionToClangd
198
190
199
- #if os(Windows)
200
- self . hClangd = process. processHandle
201
- #else
202
- self . clangdPid = process. processIdentifier
203
- #endif
191
+ self . clangdProcess = process
204
192
}
205
193
206
194
/// Restart `clangd` after it has crashed.
@@ -318,21 +306,7 @@ actor ClangLanguageService: LanguageService, MessageHandler {
318
306
}
319
307
320
308
func crash( ) {
321
- // Since `clangd` doesn't have a method to crash it, terminate it.
322
- #if os(Windows)
323
- if self . hClangd != INVALID_HANDLE_VALUE {
324
- // We can potentially deadlock the process if a kobject is a pending state.
325
- // Unfortunately, the `OpenProcess(PROCESS_TERMINATE, ...)`, `CreateRemoteThread`, `ExitProcess` dance, while
326
- // safer, can also indefinitely spin as `CreateRemoteThread` may not be serviced depending on the state of
327
- // the process. This just attempts to terminate the process, risking a deadlock and resource leaks, which is fine
328
- // since we only use `crash` from tests.
329
- _ = TerminateProcess ( self . hClangd, 255 )
330
- }
331
- #else
332
- if let pid = self . clangdPid {
333
- kill ( pid, SIGKILL) // ignore-unacceptable-language
334
- }
335
- #endif
309
+ clangdProcess? . terminateImmediately ( )
336
310
}
337
311
}
338
312
@@ -401,15 +375,19 @@ extension ClangLanguageService {
401
375
}
402
376
403
377
package func shutdown( ) async {
404
- let clangd = clangd!
405
- await withCheckedContinuation { continuation in
406
- _ = clangd. send ( ShutdownRequest ( ) ) { _ in
407
- Task {
408
- clangd. send ( ExitNotification ( ) )
409
- continuation. resume ( )
410
- }
378
+ _ = await orLog ( " Shutting down clangd " ) {
379
+ guard let clangd else { return }
380
+ // Give clangd 2 seconds to shut down by itself. If it doesn't shut down within that time, terminate it.
381
+ try await withTimeout ( . seconds( 2 ) ) {
382
+ _ = try await clangd. send ( ShutdownRequest ( ) )
383
+ clangd. send ( ExitNotification ( ) )
411
384
}
412
385
}
386
+ await orLog ( " Terminating clangd " ) {
387
+ // Give clangd 1 second to exit after receiving the `exit` notification. If it doesn't exit within that time,
388
+ // terminate it.
389
+ try await clangdProcess? . terminateIfRunning ( after: . seconds( 1 ) )
390
+ }
413
391
}
414
392
415
393
// MARK: - Text synchronization
0 commit comments