@@ -12,7 +12,8 @@ module Language.LSP.Server.Control (
1212import Colog.Core (LogAction (.. ), Severity (.. ), WithSeverity (.. ), (<&) )
1313import Colog.Core qualified as L
1414import Control.Applicative ((<|>) )
15- import Control.Concurrent.Async (withAsync )
15+ import Control.Concurrent.Async (withAsync , wait , cancel , race )
16+ import Control.Concurrent (threadDelay )
1617import Control.Concurrent.STM.TChan
1718import Control.Exception (catchJust , throwIO )
1819import Control.Monad.IO.Class
@@ -44,6 +45,7 @@ data LspServerLog
4445 | BrokenPipeWhileSending TL. Text -- truncated outgoing message (including header)
4546 | Starting
4647 | ServerStopped
48+ | SenderShutdownTimeout -- client sender did not stop in time
4749 | ParsedMsg T. Text
4850 | SendMsg TL. Text
4951 deriving (Show )
@@ -70,6 +72,7 @@ instance Pretty LspServerLog where
7072 pretty Starting = " Server starting"
7173 pretty (ParsedMsg msg) = " ---> " <> pretty msg
7274 pretty (SendMsg msg) = " <--2-- " <> pretty msg
75+ pretty SenderShutdownTimeout = " Sender did not stop within 3s; cancelling"
7376
7477-- ---------------------------------------------------------------------
7578
@@ -150,6 +153,14 @@ runServerWith ioLogger logger clientIn clientOut serverDefinition = do
150153 withAsync (sendServer ioLogger cout clientOut) $ \ _sendAsync -> do
151154 let sendMsg = atomically . writeTChan cout
152155 res <- ioLoop ioLogger logger clientIn serverDefinition emptyVFS sendMsg
156+ -- The sender should stop after we send the shutdown response.
157+ -- Wait up to 3 seconds for the sender to finish; cancel if it doesn't.
158+ r <- race (wait _sendAsync) (threadDelay 3_000_000 )
159+ case r of
160+ Left _ -> pure ()
161+ Right _ -> do
162+ ioLogger <& SenderShutdownTimeout `WithSeverity ` Warning
163+ cancel _sendAsync
153164 ioLogger <& ServerStopped `WithSeverity ` Info
154165 return res
155166
0 commit comments