Skip to content

Commit a044156

Browse files
committed
Release hiedb on shutdown
1 parent 870df21 commit a044156

File tree

1 file changed

+25
-14
lines changed

1 file changed

+25
-14
lines changed

ghcide/src/Development/IDE/LSP/LanguageServer.hs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import Development.IDE.Core.Tracing
3838
import Development.IDE.LSP.HoverDefinition
3939
import Development.IDE.Types.Logger
4040

41+
import Control.Monad.IO.Unlift (MonadUnliftIO)
4142
import System.IO.Unsafe (unsafeInterleaveIO)
4243

4344
issueTrackerUrl :: T.Text
@@ -57,11 +58,16 @@ runLanguageServer
5758
runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChange userHandlers getIdeState = do
5859

5960
-- 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.
6162
clientMsgVar <- newEmptyMVar
6263
-- Forcefully exit
6364
let exit = void $ tryPutMVar clientMsgVar ()
6465

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+
6571
-- The set of requests ids that we have received but not finished processing
6672
pendingRequests <- newTVarIO Set.empty
6773
-- The set of requests that have been cancelled and are also in pendingRequests
@@ -96,7 +102,7 @@ runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChan
96102
[ ideHandlers
97103
, cancelHandler cancelRequest
98104
, exitHandler exit
99-
, shutdownHandler
105+
, shutdownHandler stopReactorLoop
100106
]
101107
-- Cancel requests are special since they need to be handled
102108
-- out of order to be useful. Existing handlers are run afterwards.
@@ -105,25 +111,23 @@ runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChan
105111
let serverDefinition = LSP.ServerDefinition
106112
{ LSP.onConfigurationChange = onConfigurationChange
107113
, LSP.defaultConfig = defaultConfig
108-
, LSP.doInitialize = handleInit exit clearReqId waitForCancel clientMsgChan
114+
, LSP.doInitialize = handleInit reactorLifetime exit clearReqId waitForCancel clientMsgChan
109115
, LSP.staticHandlers = asyncHandlers
110116
, LSP.interpretHandler = \(env, st) -> LSP.Iso (LSP.runLspT env . flip runReaderT (clientMsgChan,st)) liftIO
111117
, LSP.options = modifyOptions options
112118
}
113119

114-
void $ waitAnyCancel =<< traverse async
115-
[ void $ LSP.runServerWithHandles
120+
void $ untilMVar clientMsgVar $
121+
void $ LSP.runServerWithHandles
116122
inH
117123
outH
118124
serverDefinition
119-
, void $ readMVar clientMsgVar
120-
]
121125

122126
where
123127
handleInit
124-
:: IO () -> (SomeLspId -> IO ()) -> (SomeLspId -> IO ()) -> Chan ReactorMessage
128+
:: MVar () -> IO () -> (SomeLspId -> IO ()) -> (SomeLspId -> IO ()) -> Chan ReactorMessage
125129
-> 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
127131
traceWithSpan sp params
128132
let root = LSP.resRootPath env
129133
dir <- maybe getCurrentDirectory return root
@@ -145,7 +149,7 @@ runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChan
145149
T.pack $ "Fatal error in server thread: " <> show e
146150
sendErrorMessage e
147151
exitClientMsg
148-
handleServerException _ = pure ()
152+
handleServerException (Right _) = pure ()
149153

150154
sendErrorMessage (e :: SomeException) = do
151155
LSP.runLspT env $ LSP.sendNotification SWindowShowMessage $
@@ -178,7 +182,7 @@ runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChan
178182
) $ \(e :: SomeException) -> do
179183
exceptionInHandler e
180184
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
182186
putMVar dbMVar (hiedb,hieChan)
183187
forever $ do
184188
msg <- readChan clientMsgChan
@@ -190,15 +194,22 @@ runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChan
190194
pure $ Right (env,ide)
191195

192196

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 ]
193202

194203
cancelHandler :: (SomeLspId -> IO ()) -> LSP.Handlers (ServerM c)
195204
cancelHandler cancelRequest = LSP.notificationHandler SCancelRequest $ \NotificationMessage{_params=CancelParams{_id}} ->
196205
liftIO $ cancelRequest (SomeLspId _id)
197206

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
200209
(_, 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
202213
-- flush out the Shake session to record a Shake profile if applicable
203214
liftIO $ shakeShut ide
204215
resp $ Right Empty

0 commit comments

Comments
 (0)