@@ -38,12 +38,8 @@ import Development.IDE.Core.Tracing
3838import Development.IDE.LSP.HoverDefinition
3939import Development.IDE.Types.Logger
4040
41- import Control.Monad.IO.Unlift (MonadUnliftIO )
4241import System.IO.Unsafe (unsafeInterleaveIO )
4342
44- issueTrackerUrl :: T. Text
45- issueTrackerUrl = " https://github.com/haskell/haskell-language-server/issues"
46-
4743runLanguageServer
4844 :: forall config . (Show config )
4945 => LSP. Options
@@ -58,16 +54,11 @@ runLanguageServer
5854runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChange userHandlers getIdeState = do
5955
6056 -- This MVar becomes full when the server thread exits or we receive exit message from client.
61- -- LSP server will be canceled when it's full.
57+ -- LSP loop will be canceled when it's full.
6258 clientMsgVar <- newEmptyMVar
6359 -- Forcefully exit
6460 let exit = void $ tryPutMVar clientMsgVar ()
6561
66- -- An MVar to control the lifetime of the reactor loop.
67- -- The loop will be stopped and resources freed when it's full
68- reactorLifetime <- newEmptyMVar
69- let stopReactorLoop = void $ tryPutMVar reactorLifetime ()
70-
7162 -- The set of requests ids that we have received but not finished processing
7263 pendingRequests <- newTVarIO Set. empty
7364 -- The set of requests that have been cancelled and are also in pendingRequests
@@ -102,7 +93,7 @@ runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChan
10293 [ ideHandlers
10394 , cancelHandler cancelRequest
10495 , exitHandler exit
105- , shutdownHandler stopReactorLoop
96+ , shutdownHandler
10697 ]
10798 -- Cancel requests are special since they need to be handled
10899 -- out of order to be useful. Existing handlers are run afterwards.
@@ -111,23 +102,25 @@ runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChan
111102 let serverDefinition = LSP. ServerDefinition
112103 { LSP. onConfigurationChange = onConfigurationChange
113104 , LSP. defaultConfig = defaultConfig
114- , LSP. doInitialize = handleInit reactorLifetime exit clearReqId waitForCancel clientMsgChan
105+ , LSP. doInitialize = handleInit exit clearReqId waitForCancel clientMsgChan
115106 , LSP. staticHandlers = asyncHandlers
116107 , LSP. interpretHandler = \ (env, st) -> LSP. Iso (LSP. runLspT env . flip runReaderT (clientMsgChan,st)) liftIO
117108 , LSP. options = modifyOptions options
118109 }
119110
120- void $ untilMVar clientMsgVar $
121- void $ LSP. runServerWithHandles
111+ void $ waitAnyCancel =<< traverse async
112+ [ void $ LSP. runServerWithHandles
122113 inH
123114 outH
124115 serverDefinition
116+ , void $ readMVar clientMsgVar
117+ ]
125118
126119 where
127120 handleInit
128- :: MVar () -> IO () -> (SomeLspId -> IO () ) -> (SomeLspId -> IO () ) -> Chan ReactorMessage
121+ :: IO () -> (SomeLspId -> IO () ) -> (SomeLspId -> IO () ) -> Chan ReactorMessage
129122 -> LSP. LanguageContextEnv config -> RequestMessage Initialize -> IO (Either err (LSP. LanguageContextEnv config , IdeState ))
130- handleInit lifetime exitClientMsg clearReqId waitForCancel clientMsgChan env (RequestMessage _ _ m params) = otTracedHandler " Initialize" (show m) $ \ sp -> do
123+ handleInit exitClientMsg clearReqId waitForCancel clientMsgChan env (RequestMessage _ _ m params) = otTracedHandler " Initialize" (show m) $ \ sp -> do
131124 traceWithSpan sp params
132125 let root = LSP. resRootPath env
133126 dir <- maybe getCurrentDirectory return root
@@ -145,71 +138,58 @@ runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChan
145138 registerIdeConfiguration (shakeExtras ide) initConfig
146139
147140 let handleServerException (Left e) = do
148- logError logger $
141+ logError (ideLogger ide) $
149142 T. pack $ " Fatal error in server thread: " <> show e
150- sendErrorMessage e
151143 exitClientMsg
152- handleServerException (Right _) = pure ()
153-
154- sendErrorMessage (e :: SomeException ) = do
155- LSP. runLspT env $ LSP. sendNotification SWindowShowMessage $
156- ShowMessageParams MtError $ T. unlines
157- [ " Unhandled exception, please [report](" <> issueTrackerUrl <> " ): "
158- , T. pack(show e)
159- ]
160-
161- exceptionInHandler e = do
162- logError logger $ T. pack $
163- " Unexpected exception, please report!\n " ++
164- " Exception: " ++ show e
165- sendErrorMessage e
166-
144+ handleServerException _ = pure ()
167145 logger = ideLogger ide
168-
169- checkCancelled _id act k =
170- flip finally (clearReqId _id) $
171- catch (do
172- -- We could optimize this by first checking if the id
173- -- is in the cancelled set. However, this is unlikely to be a
174- -- bottleneck and the additional check might hide
175- -- issues with async exceptions that need to be fixed.
176- cancelOrRes <- race (waitForCancel _id) act
177- case cancelOrRes of
178- Left () -> do
179- logDebug (ideLogger ide) $ T. pack $ " Cancelled request " <> show _id
180- k $ ResponseError RequestCancelled " " Nothing
181- Right res -> pure res
182- ) $ \ (e :: SomeException ) -> do
183- exceptionInHandler e
184- k $ ResponseError InternalError (T. pack $ show e) Nothing
185- _ <- flip forkFinally handleServerException $ untilMVar lifetime $ runWithDb logger dbLoc $ \ hiedb hieChan -> do
146+ _ <- flip forkFinally handleServerException $ runWithDb logger dbLoc $ \ hiedb hieChan -> do
186147 putMVar dbMVar (hiedb,hieChan)
187148 forever $ do
188149 msg <- readChan clientMsgChan
189150 -- We dispatch notifications synchronously and requests asynchronously
190151 -- This is to ensure that all file edits and config changes are applied before a request is handled
191152 case msg of
192- ReactorNotification act -> handle exceptionInHandler act
193- ReactorRequest _id act k -> void $ async $ checkCancelled _id act k
153+ ReactorNotification act -> do
154+ catch act $ \ (e :: SomeException ) ->
155+ logError (ideLogger ide) $ T. pack $
156+ " Unexpected exception on notification, please report!\n " ++
157+ " Exception: " ++ show e
158+ ReactorRequest _id act k -> void $ async $
159+ checkCancelled ide clearReqId waitForCancel _id act k
194160 pure $ Right (env,ide)
195161
162+ checkCancelled
163+ :: IdeState -> (SomeLspId -> IO () ) -> (SomeLspId -> IO () ) -> SomeLspId
164+ -> IO () -> (ResponseError -> IO () ) -> IO ()
165+ checkCancelled ide clearReqId waitForCancel _id act k =
166+ flip finally (clearReqId _id) $
167+ catch (do
168+ -- We could optimize this by first checking if the id
169+ -- is in the cancelled set. However, this is unlikely to be a
170+ -- bottleneck and the additional check might hide
171+ -- issues with async exceptions that need to be fixed.
172+ cancelOrRes <- race (waitForCancel _id) act
173+ case cancelOrRes of
174+ Left () -> do
175+ logDebug (ideLogger ide) $ T. pack $ " Cancelled request " <> show _id
176+ k $ ResponseError RequestCancelled " " Nothing
177+ Right res -> pure res
178+ ) $ \ (e :: SomeException ) -> do
179+ logError (ideLogger ide) $ T. pack $
180+ " Unexpected exception on request, please report!\n " ++
181+ " Exception: " ++ show e
182+ k $ ResponseError InternalError (T. pack $ show e) Nothing
196183
197- -- | Runs the action until it ends or until the given MVar is put.
198- -- Rethrows any exceptions.
199- untilMVar :: MonadUnliftIO m => MVar () -> m () -> m ()
200- untilMVar mvar io = void $
201- waitAnyCancel =<< traverse async [ io , readMVar mvar ]
202184
203185cancelHandler :: (SomeLspId -> IO () ) -> LSP. Handlers (ServerM c )
204186cancelHandler cancelRequest = LSP. notificationHandler SCancelRequest $ \ NotificationMessage {_params= CancelParams {_id}} ->
205187 liftIO $ cancelRequest (SomeLspId _id)
206188
207- shutdownHandler :: IO () -> LSP. Handlers (ServerM c )
208- shutdownHandler stopReactor = LSP. requestHandler SShutdown $ \ _ resp -> do
189+ shutdownHandler :: LSP. Handlers (ServerM c )
190+ shutdownHandler = LSP. requestHandler SShutdown $ \ _ resp -> do
209191 (_, ide) <- ask
210- liftIO $ logDebug (ideLogger ide) " Received shutdown message"
211- -- stop the reactor to free up the hiedb connection
212- liftIO stopReactor
192+ liftIO $ logDebug (ideLogger ide) " Received exit message"
213193 -- flush out the Shake session to record a Shake profile if applicable
214194 liftIO $ shakeShut ide
215195 resp $ Right Empty
0 commit comments