@@ -73,15 +73,21 @@ module DAP.Adaptor
73
73
-- * Internal use
74
74
, send
75
75
, sendRaw
76
+ -- * Internal function used to execute actions on behalf of the DAP server
77
+ -- from child threads (useful for handling asynchronous debugger events).
78
+ , runAdaptorWith
76
79
) where
77
80
----------------------------------------------------------------------------
78
81
import Control.Concurrent ( ThreadId )
82
+ import Control.Concurrent.MVar ( newMVar , newEmptyMVar , modifyMVar_
83
+ , takeMVar , putMVar , readMVar , MVar )
79
84
import Control.Concurrent.Lifted ( fork , killThread )
80
85
import Control.Exception ( throwIO )
81
86
import Control.Concurrent.STM ( atomically , readTVarIO , modifyTVar' )
82
87
import Control.Monad ( when , unless )
83
- import Control.Monad.Except ( throwError )
84
- import Control.Monad.State.Strict ( MonadIO (liftIO ), gets , modify' , put )
88
+ import Control.Monad.Except ( runExceptT , throwError )
89
+ import Control.Monad.State ( evalStateT , runStateT , execStateT , gets
90
+ , MonadIO (liftIO ), gets , modify' , put )
85
91
import Data.Aeson ( FromJSON , Result (.. ), fromJSON )
86
92
import Data.Maybe ( fromMaybe )
87
93
import Data.Aeson.Encode.Pretty ( encodePretty )
@@ -183,18 +189,30 @@ registerNewDebugSession
183
189
-> app
184
190
-> IO ()
185
191
-- ^ Action to run debugger (operates in a forked thread that gets killed when disconnect is set)
186
- -> IO ()
192
+ -> (( Adaptor app () -> IO () ) -> IO () )
187
193
-- ^ Long running operation, meant to be used as a sink for
188
194
-- the debugger to emit events and for the adaptor to forward to the editor
189
195
-- This function should be in a 'forever' loop waiting on the read end of
190
196
-- a debugger channel.
197
+ --
198
+ -- This event handler thread also takes an argument that allows any child thread to execute
199
+ -- events on behalf of the DAP server (in 'Adaptor app ()'). This function should always be
200
+ -- used when sending events to the editor from the debugger (or from any forked thread).
201
+ --
202
+ -- >
203
+ -- > registerNewDebugSession sessionId appState loadDebugger $ \withAdaptor ->
204
+ -- > forever $ getDebuggerOutput >>= \output -> do
205
+ -- > withAdaptor $ sendOutputEvent defaultOutputEvent { outputEventOutput = output }
206
+ -- >
207
+ --
191
208
-> Adaptor app ()
192
209
registerNewDebugSession k v debuggerExecution outputEventSink = do
193
210
store <- gets appStore
211
+ adaptorStateMVar <- gets adaptorStateMVar
194
212
debuggerThreadState <- liftIO $
195
213
DebuggerThreadState
196
214
<$> fork debuggerExecution
197
- <*> fork outputEventSink
215
+ <*> fork ( outputEventSink (runAdaptorWith adaptorStateMVar))
198
216
liftIO . atomically $ modifyTVar' store (H. insert k (debuggerThreadState, v))
199
217
setDebugSessionId k
200
218
logInfo $ BL8. pack $ " Registered new debug session: " <> unpack k
@@ -539,3 +557,18 @@ addSourcePathBySourceReferenceId path sourceId =
539
557
modify' $ \ s -> s
540
558
{ sourceReferencesMap = I. insert sourceId path (sourceReferencesMap s)
541
559
}
560
+
561
+ ----------------------------------------------------------------------------
562
+ -- | Evaluates Adaptor action by using and updating the state in the MVar
563
+ runAdaptorWith :: MVar (AdaptorState app ) -> Adaptor app () -> IO ()
564
+ runAdaptorWith adaptorStateMVar action = do
565
+ modifyMVar_ adaptorStateMVar (flip runAdaptor action)
566
+
567
+ ----------------------------------------------------------------------------
568
+ -- | Utility for evaluating a monad transformer stack
569
+ runAdaptor :: AdaptorState app -> Adaptor app () -> IO (AdaptorState app )
570
+ runAdaptor adaptorState (Adaptor client) =
571
+ runStateT (runExceptT client) adaptorState >>= \ case
572
+ (Left (errorMessage, maybeMessage), nextState) ->
573
+ runAdaptor nextState (sendErrorResponse errorMessage maybeMessage)
574
+ (Right () , nextState) -> pure nextState
0 commit comments