@@ -100,12 +100,8 @@ actor ClangLanguageService: LanguageService, MessageHandler {
100100 /// Type to map `clangd`'s semantic token legend to SourceKit-LSP's.
101101 private var semanticTokensTranslator : SemanticTokensLegendTranslator ? = nil
102102
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 ?
109105
110106 /// Creates a language server for the given client referencing the clang binary specified in `toolchain`.
111107 /// Returns `nil` if `clangd` can't be found.
@@ -159,11 +155,7 @@ actor ClangLanguageService: LanguageService, MessageHandler {
159155 ///
160156 /// - Parameter terminationStatus: The exit code of `clangd`.
161157 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
167159 if terminationStatus != 0 {
168160 self . state = . connectionInterrupted
169161 self . restartClangd ( )
@@ -196,11 +188,7 @@ actor ClangLanguageService: LanguageService, MessageHandler {
196188 )
197189 self . clangd = connectionToClangd
198190
199- #if os(Windows)
200- self . hClangd = process. processHandle
201- #else
202- self . clangdPid = process. processIdentifier
203- #endif
191+ self . clangdProcess = process
204192 }
205193
206194 /// Restart `clangd` after it has crashed.
@@ -318,21 +306,7 @@ actor ClangLanguageService: LanguageService, MessageHandler {
318306 }
319307
320308 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 ( )
336310 }
337311}
338312
@@ -401,15 +375,19 @@ extension ClangLanguageService {
401375 }
402376
403377 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 ( ) )
411384 }
412385 }
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+ }
413391 }
414392
415393 // MARK: - Text synchronization
0 commit comments