Skip to content

Commit 9fb399b

Browse files
authored
Merge pull request #11 from mpickering/wip/romes/wip
Allow a client to explicitly terminate the server
2 parents 8727be8 + 800623f commit 9fb399b

File tree

1 file changed

+36
-12
lines changed

1 file changed

+36
-12
lines changed

src/DAP/Server.hs

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,26 @@
1515
{-# LANGUAGE NamedFieldPuns #-}
1616
{-# LANGUAGE DeriveGeneric #-}
1717
{-# LANGUAGE LambdaCase #-}
18+
{-# LANGUAGE ViewPatterns #-}
1819
----------------------------------------------------------------------------
1920
module DAP.Server
2021
( runDAPServer
2122
, runDAPServerWithLogger
2223
, readPayload
24+
, TerminateServer(..)
2325
) where
2426
----------------------------------------------------------------------------
2527
import Control.Monad ( when, forever )
28+
import Control.Concurrent ( ThreadId, myThreadId, throwTo )
2629
import Control.Concurrent.MVar ( newMVar )
2730
import Control.Concurrent.STM ( newTVarIO )
28-
import Control.Exception ( SomeException
31+
import Control.Exception ( Exception
32+
, SomeAsyncException(..)
33+
, SomeException
2934
, IOException
3035
, catch
3136
, fromException
37+
, toException
3238
, throwIO )
3339
import Control.Monad ( void )
3440
import Data.Aeson ( decodeStrict, eitherDecode, Value, FromJSON )
@@ -42,6 +48,7 @@ import System.IO ( hClose, hSetNewlineMode, Handle, N
4248
, NewlineMode(NewlineMode, outputNL, inputNL)
4349
, IOMode(ReadWriteMode), stderr, hPrint)
4450
import System.IO.Error ( isEOFError )
51+
import System.Exit ( exitWith, ExitCode(ExitSuccess) )
4552
import Text.Read ( readMaybe )
4653
import qualified Data.ByteString.Lazy.Char8 as BL8
4754
import qualified Data.ByteString.Char8 as BS
@@ -63,7 +70,11 @@ stdoutLogger = do
6370
withLock handleLock $ do
6471
T.putStrLn msg
6572

66-
73+
-- | An exception to throw if you want to stop the server execution from a
74+
-- client. This is useful if you launch a new server per debugging session and
75+
-- want to terminate it at the end.
76+
data TerminateServer = TerminateServer
77+
deriving (Show, Exception)
6778

6879
runDAPServer :: ServerConfig -> (Command -> Adaptor app Request ()) -> IO ()
6980
runDAPServer config communicate = do
@@ -81,13 +92,19 @@ runDAPServerWithLogger rawLogAction serverConfig@ServerConfig {..} communicate =
8192
let logAction = cfilter (\msg -> if debugLogging then True else severity msg /= DEBUG) rawLogAction
8293
logAction <& (mkDebugMessage $ (T.pack ("Running DAP server on " <> show port <> "...")))
8394
appStore <- newTVarIO mempty
84-
serve (Host host) (show port) $ \(socket, address) -> do
85-
logAction <& mkDebugMessage (T.pack ("TCP connection established from " ++ show address))
86-
handle <- socketToHandle socket ReadWriteMode
87-
hSetNewlineMode handle NewlineMode { inputNL = CRLF, outputNL = CRLF }
88-
adaptorStateMVar <- initAdaptorState logAction handle address appStore serverConfig
89-
serviceClient communicate adaptorStateMVar
90-
`catch` exceptionHandler logAction handle address debugLogging
95+
mainThread <- myThreadId
96+
let
97+
server = serve (Host host) (show port) $ \(socket, address) -> do
98+
logAction <& mkDebugMessage (T.pack ("TCP connection established from " ++ show address))
99+
handle <- socketToHandle socket ReadWriteMode
100+
hSetNewlineMode handle NewlineMode { inputNL = CRLF, outputNL = CRLF }
101+
adaptorStateMVar <- initAdaptorState logAction handle address appStore serverConfig
102+
serviceClient communicate adaptorStateMVar
103+
`catch` exceptionHandler logAction handle address debugLogging mainThread
104+
server `catch` \(SomeAsyncException e) ->
105+
case fromException $ toException e of
106+
Just TerminateServer -> exitWith ExitSuccess
107+
_ -> throwIO e
91108

92109
-- | Initializes the Adaptor
93110
--
@@ -120,11 +137,18 @@ serviceClient communicate lcl = forever $ runAdaptorWith lcl st $ do
120137
where
121138
st = AdaptorState MessageTypeResponse []
122139
----------------------------------------------------------------------------
123-
-- | Handle exceptions from client threads, parse and log accordingly
124-
exceptionHandler :: LogAction IO DAPLog -> Handle -> SockAddr -> Bool -> SomeException -> IO ()
125-
exceptionHandler logAction handle address shouldLog (e :: SomeException) = do
140+
-- | Handle exceptions from client threads, parse and log accordingly.
141+
-- Detects if client failed with `TerminateServer` and kills the server accordingly by sending an exception to the main thread.
142+
exceptionHandler :: LogAction IO DAPLog -> Handle -> SockAddr -> Bool -> ThreadId -> SomeException -> IO ()
143+
exceptionHandler logAction handle address shouldLog serverThread (e :: SomeException) = do
126144
let
127145
dumpError
146+
| Just TerminateServer <- fromException e
147+
= do
148+
logger logAction ERROR address Nothing
149+
$ withBraces
150+
$ T.pack ("Server terminated!")
151+
throwTo serverThread (SomeAsyncException TerminateServer)
128152
| Just (ParseException msg) <- fromException e
129153
= logger logAction ERROR address Nothing
130154
$ withBraces

0 commit comments

Comments
 (0)