diff --git a/src/Quasar/Exceptions/ExceptionChannel.hs b/src/Quasar/Exceptions/ExceptionChannel.hs index 4974c2e9549b1d9da3d0218e94722139195fc2a4..3109a90867697ea479d227112a882cfb681f871d 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."