@@ -28,13 +28,13 @@ module Development.IDE.Types.Logger
2828 , renderStrict
2929 ) where
3030
31- import Control.Concurrent (MVar , myThreadId , tryReadMVar )
31+ import Control.Concurrent (myThreadId )
3232import Control.Concurrent.Extra (Lock , newLock , withLock )
3333import Control.Concurrent.STM (atomically , newTQueueIO ,
34- writeTQueue )
34+ writeTQueue , newTVarIO , writeTVar , readTVarIO , newTBQueueIO , flushTBQueue , writeTBQueue , isFullTBQueue )
3535import Control.Concurrent.STM.TQueue (flushTQueue )
3636import Control.Exception (IOException )
37- import Control.Monad (forM_ , when , (>=>) )
37+ import Control.Monad (forM_ , when , (>=>) , unless )
3838import Control.Monad.IO.Class (MonadIO (liftIO ))
3939import Data.Foldable (for_ )
4040import Data.Functor.Contravariant (Contravariant (contramap ))
@@ -304,22 +304,37 @@ textWithPriorityToText columns WithPriority{ priority, callStack_, payload } = d
304304 PriorityColumn -> pure (priorityToText priority)
305305 DataColumn -> pure payload
306306
307- -- | Given a 'Recorder' that requires an argument, and an 'MVar' that
308- -- will eventually be filled with that argument, produces a 'Recorder'
309- -- that queues up messages until the argument is available, at which
310- -- point it sends the backlog.
311- withBacklog :: MVar v -> (v -> Recorder a ) -> IO (Recorder a )
312- withBacklog argVar recFun = do
313- backlog <- newTQueueIO
314- pure $ Recorder $ \ it -> do
315- marg <- liftIO $ tryReadMVar argVar
316- case marg of
317- Nothing -> liftIO $ atomically $ writeTQueue backlog it
318- Just arg -> do
319- let recorder = recFun arg
320- toRecord <- liftIO $ atomically $ flushTQueue backlog
321- for_ toRecord (logger_ recorder)
322- logger_ recorder it
307+ -- | Given a 'Recorder' that requires an argument, produces a 'Recorder'
308+ -- that queues up messages until the argument is provided using the callback, at which
309+ -- point it sends the backlog and begins functioning normally.
310+ withBacklog :: (v -> Recorder a ) -> IO (Recorder a , v -> IO () )
311+ withBacklog recFun = do
312+ -- Arbitrary backlog capacity
313+ backlog <- newTBQueueIO 100
314+ let backlogRecorder = Recorder $ \ it -> liftIO $ atomically $ do
315+ -- If the queue is full just drop the message on the floor. This is most likely
316+ -- to happen if the callback is just never going to be called; in which case
317+ -- we want neither to build up an unbounded backlog in memory, nor block waiting
318+ -- for space!
319+ full <- isFullTBQueue backlog
320+ unless full $ writeTBQueue backlog it
321+
322+ -- The variable holding the recorder starts out holding the recorder that writes
323+ -- to the backlog.
324+ recVar <- newTVarIO backlogRecorder
325+ -- The callback atomically swaps out the recorder for the final one, and flushes
326+ -- the backlog to it.
327+ let cb arg = do
328+ let recorder = recFun arg
329+ toRecord <- atomically $ writeTVar recVar recorder >> flushTBQueue backlog
330+ for_ toRecord (logger_ recorder)
331+
332+ -- The recorder we actually return looks in the variable and uses whatever is there.
333+ let varRecorder = Recorder $ \ it -> do
334+ r <- liftIO $ readTVarIO recVar
335+ logger_ r it
336+
337+ pure (varRecorder, cb)
323338
324339-- | Creates a recorder that sends logs to the LSP client via @window/showMessage@ notifications.
325340lspClientMessageRecorder :: LanguageContextEnv config -> Recorder (WithPriority Text )
0 commit comments