From 19653c9f83d8f419b14fc3d6d9cfd73cc033e7f8 Mon Sep 17 00:00:00 2001 From: Jens Nolte <git@queezle.net> Date: Tue, 22 Feb 2022 02:20:26 +0100 Subject: [PATCH] Add ExceptionChannel helper implementations --- src/Quasar/Exceptions/ExceptionChannel.hs | 41 +++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/Quasar/Exceptions/ExceptionChannel.hs b/src/Quasar/Exceptions/ExceptionChannel.hs index 4974c2e..3109a90 100644 --- a/src/Quasar/Exceptions/ExceptionChannel.hs +++ b/src/Quasar/Exceptions/ExceptionChannel.hs @@ -1,6 +1,9 @@ module Quasar.Exceptions.ExceptionChannel ( panicChannel, loggingExceptionChannel, + newExceptionChannelWitness, + newExceptionRedirector, + newExceptionCollector ) where import Control.Concurrent (forkIO) @@ -36,3 +39,41 @@ loggingExceptionChannel worker = where logFn :: SomeException -> ShortIO () logFn ex = unsafeShortIO $ Trace.traceIO $ displayException ex + +newExceptionChannelWitness :: ExceptionChannel -> STM (ExceptionChannel, STM Bool) +newExceptionChannelWitness exChan = do + var <- newTVar False + let chan = ExceptionChannel \ex -> lock var >> throwToExceptionChannel exChan ex + pure (chan, readTVar var) + where + lock :: TVar Bool -> STM () + lock var = unlessM (readTVar var) (writeTVar var True) + +newExceptionRedirector :: ExceptionChannel -> STM (ExceptionChannel, ExceptionChannel -> STM ()) +newExceptionRedirector initialExceptionChannel = do + channelVar <- newTVar initialExceptionChannel + pure (ExceptionChannel (channelFn channelVar), writeTVar channelVar) + where + channelFn :: TVar ExceptionChannel -> SomeException -> STM () + channelFn channelVar ex = do + channel <- readTVar channelVar + throwToExceptionChannel channel ex + +-- | Collects exceptions. After they have been collected (by using the resulting +-- transaction), further exceptions are forwarded to the backup exception sink. +-- The collection transaction may only be used once. +newExceptionCollector :: ExceptionChannel -> STM (ExceptionChannel, STM [SomeException]) +newExceptionCollector backupExceptionChannel = do + exceptionsVar <- newTVar (Just []) + pure (ExceptionChannel (channelFn exceptionsVar), gatherResult exceptionsVar) + where + channelFn :: TVar (Maybe [SomeException]) -> SomeException -> STM () + channelFn exceptionsVar ex = do + readTVar exceptionsVar >>= \case + Just exceptions -> writeTVar exceptionsVar (Just (ex : exceptions)) + Nothing -> throwToExceptionChannel backupExceptionChannel ex + gatherResult :: TVar (Maybe [SomeException]) -> STM [SomeException] + gatherResult exceptionsVar = + swapTVar exceptionsVar Nothing >>= \case + Just exceptions -> pure exceptions + Nothing -> throwSTM $ userError "Exception collector result can only be generated once." -- GitLab