@@ -38,6 +38,7 @@ import Development.IDE.Core.Tracing
38
38
import Development.IDE.LSP.HoverDefinition
39
39
import Development.IDE.Types.Logger
40
40
41
+ import Control.Monad.IO.Unlift (MonadUnliftIO )
41
42
import System.IO.Unsafe (unsafeInterleaveIO )
42
43
43
44
issueTrackerUrl :: T. Text
@@ -57,11 +58,16 @@ runLanguageServer
57
58
runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChange userHandlers getIdeState = do
58
59
59
60
-- This MVar becomes full when the server thread exits or we receive exit message from client.
60
- -- LSP loop will be canceled when it's full.
61
+ -- LSP server will be canceled when it's full.
61
62
clientMsgVar <- newEmptyMVar
62
63
-- Forcefully exit
63
64
let exit = void $ tryPutMVar clientMsgVar ()
64
65
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
+
65
71
-- The set of requests ids that we have received but not finished processing
66
72
pendingRequests <- newTVarIO Set. empty
67
73
-- The set of requests that have been cancelled and are also in pendingRequests
@@ -96,7 +102,7 @@ runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChan
96
102
[ ideHandlers
97
103
, cancelHandler cancelRequest
98
104
, exitHandler exit
99
- , shutdownHandler
105
+ , shutdownHandler stopReactorLoop
100
106
]
101
107
-- Cancel requests are special since they need to be handled
102
108
-- out of order to be useful. Existing handlers are run afterwards.
@@ -105,25 +111,23 @@ runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChan
105
111
let serverDefinition = LSP. ServerDefinition
106
112
{ LSP. onConfigurationChange = onConfigurationChange
107
113
, LSP. defaultConfig = defaultConfig
108
- , LSP. doInitialize = handleInit exit clearReqId waitForCancel clientMsgChan
114
+ , LSP. doInitialize = handleInit reactorLifetime exit clearReqId waitForCancel clientMsgChan
109
115
, LSP. staticHandlers = asyncHandlers
110
116
, LSP. interpretHandler = \ (env, st) -> LSP. Iso (LSP. runLspT env . flip runReaderT (clientMsgChan,st)) liftIO
111
117
, LSP. options = modifyOptions options
112
118
}
113
119
114
- void $ waitAnyCancel =<< traverse async
115
- [ void $ LSP. runServerWithHandles
120
+ void $ untilMVar clientMsgVar $
121
+ void $ LSP. runServerWithHandles
116
122
inH
117
123
outH
118
124
serverDefinition
119
- , void $ readMVar clientMsgVar
120
- ]
121
125
122
126
where
123
127
handleInit
124
- :: IO () -> (SomeLspId -> IO () ) -> (SomeLspId -> IO () ) -> Chan ReactorMessage
128
+ :: MVar () -> IO () -> (SomeLspId -> IO () ) -> (SomeLspId -> IO () ) -> Chan ReactorMessage
125
129
-> LSP. LanguageContextEnv config -> RequestMessage Initialize -> IO (Either err (LSP. LanguageContextEnv config , IdeState ))
126
- handleInit exitClientMsg clearReqId waitForCancel clientMsgChan env (RequestMessage _ _ m params) = otTracedHandler " Initialize" (show m) $ \ sp -> do
130
+ handleInit lifetime exitClientMsg clearReqId waitForCancel clientMsgChan env (RequestMessage _ _ m params) = otTracedHandler " Initialize" (show m) $ \ sp -> do
127
131
traceWithSpan sp params
128
132
let root = LSP. resRootPath env
129
133
dir <- maybe getCurrentDirectory return root
@@ -145,7 +149,7 @@ runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChan
145
149
T. pack $ " Fatal error in server thread: " <> show e
146
150
sendErrorMessage e
147
151
exitClientMsg
148
- handleServerException _ = pure ()
152
+ handleServerException ( Right _) = pure ()
149
153
150
154
sendErrorMessage (e :: SomeException ) = do
151
155
LSP. runLspT env $ LSP. sendNotification SWindowShowMessage $
@@ -178,7 +182,7 @@ runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChan
178
182
) $ \ (e :: SomeException ) -> do
179
183
exceptionInHandler e
180
184
k $ ResponseError InternalError (T. pack $ show e) Nothing
181
- _ <- flip forkFinally handleServerException $ runWithDb logger dbLoc $ \ hiedb hieChan -> do
185
+ _ <- flip forkFinally handleServerException $ untilMVar lifetime $ runWithDb logger dbLoc $ \ hiedb hieChan -> do
182
186
putMVar dbMVar (hiedb,hieChan)
183
187
forever $ do
184
188
msg <- readChan clientMsgChan
@@ -190,15 +194,22 @@ runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChan
190
194
pure $ Right (env,ide)
191
195
192
196
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 ]
193
202
194
203
cancelHandler :: (SomeLspId -> IO () ) -> LSP. Handlers (ServerM c )
195
204
cancelHandler cancelRequest = LSP. notificationHandler SCancelRequest $ \ NotificationMessage {_params= CancelParams {_id}} ->
196
205
liftIO $ cancelRequest (SomeLspId _id)
197
206
198
- shutdownHandler :: LSP. Handlers (ServerM c )
199
- shutdownHandler = LSP. requestHandler SShutdown $ \ _ resp -> do
207
+ shutdownHandler :: IO () -> LSP. Handlers (ServerM c )
208
+ shutdownHandler stopReactor = LSP. requestHandler SShutdown $ \ _ resp -> do
200
209
(_, ide) <- ask
201
- liftIO $ logDebug (ideLogger ide) " Received exit message"
210
+ liftIO $ logDebug (ideLogger ide) " Received shutdown message"
211
+ -- stop the reactor to free up the hiedb connection
212
+ liftIO stopReactor
202
213
-- flush out the Shake session to record a Shake profile if applicable
203
214
liftIO $ shakeShut ide
204
215
resp $ Right Empty
0 commit comments