diff --git a/src/Quasar/Async.hs b/src/Quasar/Async.hs index 08e147e319463e8529c0da6a9b7ffe2c38fd6426..5f10464a845fa5565cd038c71e1513b189a8d05a 100644 --- a/src/Quasar/Async.hs +++ b/src/Quasar/Async.hs @@ -32,7 +32,7 @@ import Control.Exception (throwTo) data Async a = Async (Future a) Disposer instance Resource (Async a) where - getDisposer (Async _ disposer) = disposer + getDisposer (Async _ disposer) = [disposer] instance IsFuture a (Async a) where toFuture (Async awaitable _) = awaitable diff --git a/src/Quasar/Resources.hs b/src/Quasar/Resources.hs index 321351bb3724f680c46d3a0640756c720a527468..e3d8356fdf64243722c1c8dc0e19f30ef878b407 100644 --- a/src/Quasar/Resources.hs +++ b/src/Quasar/Resources.hs @@ -116,14 +116,14 @@ disposeEventually_ :: (Resource r, MonadQuasar m) => r -> m () disposeEventually_ res = ensureSTM $ disposeEventuallySTM_ res -captureResources :: MonadQuasar m => m a -> m (a, Disposer) +captureResources :: MonadQuasar m => m a -> m (a, [Disposer]) captureResources fn = do quasar <- newResourceScope localQuasar quasar do result <- fn pure (result, getDisposer (quasarResourceManager quasar)) -captureResources_ :: MonadQuasar m => m () -> m Disposer +captureResources_ :: MonadQuasar m => m () -> m [Disposer] captureResources_ fn = snd <$> captureResources fn diff --git a/src/Quasar/Resources/Disposer.hs b/src/Quasar/Resources/Disposer.hs index 71ed426d302442c0b53883433e7eee36ed94e33a..fec3fb8422882053763639b46dc752c3a4d1e647 100644 --- a/src/Quasar/Resources/Disposer.hs +++ b/src/Quasar/Resources/Disposer.hs @@ -6,8 +6,6 @@ module Quasar.Resources.Disposer ( dispose, disposeEventuallySTM, disposeEventuallySTM_, - isDisposing, - isDisposed, newUnmanagedPrimitiveDisposer, trivialDisposer, @@ -35,7 +33,13 @@ import GHC.IO (unsafePerformIO, unsafeDupablePerformIO) class Resource a where - getDisposer :: a -> Disposer + getDisposer :: a -> [Disposer] + + isDisposed :: a -> Future () + isDisposed r = foldMap isDisposed $ getDisposer r + + isDisposing :: a -> Future () + isDisposing r = awaitAny $ isDisposing <$> getDisposer r type DisposerState = TOnce DisposeFn (Future ()) @@ -46,6 +50,17 @@ data Disposer | ResourceManagerDisposer ResourceManager instance Resource Disposer where + getDisposer disposer = [disposer] + + isDisposed TrivialDisposer = pure () + isDisposed (FnDisposer _ _ _ state _) = join (toFuture state) + isDisposed (ResourceManagerDisposer resourceManager) = resourceManagerIsDisposed resourceManager + + isDisposing TrivialDisposer = pure () + isDisposing (FnDisposer _ _ _ state _) = unsafeAwaitSTM (check . isRight =<< readTOnceState state) + isDisposing (ResourceManagerDisposer resourceManager) = resourceManagerIsDisposing resourceManager + +instance Resource [Disposer] where getDisposer = id type DisposeFn = ShortIO (Future ()) @@ -65,32 +80,19 @@ dispose :: (MonadIO m, Resource r) => r -> m () dispose resource = liftIO $ await =<< atomically (disposeEventuallySTM resource) disposeEventuallySTM :: Resource r => r -> STM (Future ()) -disposeEventuallySTM resource = - case getDisposer resource of - TrivialDisposer -> pure (pure ()) - FnDisposer _ worker exChan state finalizers -> do +disposeEventuallySTM resource = mconcat <$> mapM f (getDisposer resource) + where + f :: Disposer -> STM (Future ()) + f TrivialDisposer = pure (pure ()) + f (FnDisposer _ worker exChan state finalizers) = beginDisposeFnDisposer worker exChan state finalizers - ResourceManagerDisposer resourceManager -> + f (ResourceManagerDisposer resourceManager) = beginDisposeResourceManager resourceManager disposeEventuallySTM_ :: Resource r => r -> STM () disposeEventuallySTM_ resource = void $ disposeEventuallySTM resource -isDisposed :: Resource a => a -> Future () -isDisposed resource = - case getDisposer resource of - TrivialDisposer -> pure () - FnDisposer _ _ _ state _ -> join (toFuture state) - ResourceManagerDisposer resourceManager -> resourceManagerIsDisposed resourceManager - -isDisposing :: Resource a => a -> Future () -isDisposing resource = - case getDisposer resource of - TrivialDisposer -> pure () - FnDisposer _ _ _ state _ -> unsafeAwaitSTM (check . isRight =<< readTOnceState state) - ResourceManagerDisposer resourceManager -> resourceManagerIsDisposing resourceManager - beginDisposeFnDisposer :: TIOWorker -> ExceptionSink -> DisposerState -> Finalizers -> STM (Future ()) @@ -132,7 +134,6 @@ disposerFinalizers (FnDisposer _ _ _ _ finalizers) = finalizers disposerFinalizers (ResourceManagerDisposer rm) = resourceManagerFinalizers rm - data DisposeResult = DisposeResultAwait (Future ()) | DisposeResultDependencies DisposeDependencies @@ -154,7 +155,9 @@ data ResourceManagerState | ResourceManagerDisposed instance Resource ResourceManager where - getDisposer = ResourceManagerDisposer + getDisposer rm = [ResourceManagerDisposer rm] + isDisposed = resourceManagerIsDisposed + isDisposing = resourceManagerIsDisposing newUnmanagedResourceManagerSTM :: TIOWorker -> ExceptionSink -> STM ResourceManager @@ -172,7 +175,7 @@ newUnmanagedResourceManagerSTM worker exChan = do attachResource :: Resource a => ResourceManager -> a -> STM () attachResource resourceManager resource = - attachDisposer resourceManager (getDisposer resource) + mapM_ (attachDisposer resourceManager) (getDisposer resource) attachDisposer :: ResourceManager -> Disposer -> STM () attachDisposer resourceManager disposer = do @@ -183,7 +186,7 @@ attachDisposer resourceManager disposer = do -- Returns false if the disposer is already finalized attachedFinalizer <- registerFinalizer (disposerFinalizers disposer) finalizer when attachedFinalizer $ modifyTVar attachedResources (HM.insert key disposer) - _ -> undefined -- failed to attach resource; arguably this should just dispose? + _ -> throwM $ userError "failed to attach resource" -- TODO throw proper exception where key :: Unique key = disposerKey disposer diff --git a/src/Quasar/Timer.hs b/src/Quasar/Timer.hs index 8eb408cea6912c7fdcb0235811772805bcb82e93..7b45d09296461160554dc25dd66a5e6cfb483fc5 100644 --- a/src/Quasar/Timer.hs +++ b/src/Quasar/Timer.hs @@ -50,7 +50,7 @@ instance Ord Timer where x `compare` y = time x `compare` time y instance Resource Timer where - getDisposer Timer{disposer} = disposer + getDisposer Timer{disposer} = [disposer] instance IsFuture () Timer where toFuture Timer{completed} = toFuture completed @@ -60,13 +60,13 @@ data TimerScheduler = TimerScheduler { heap :: TMVar (Heap Timer), activeCount :: TVar Int, cancelledCount :: TVar Int, - disposer :: Disposer, + thread :: Async (), ioWorker :: TIOWorker, exceptionSink :: ExceptionSink } instance Resource TimerScheduler where - getDisposer TimerScheduler{disposer} = disposer + getDisposer TimerScheduler{thread} = getDisposer thread data TimerSchedulerDisposed = TimerSchedulerDisposed deriving stock (Eq, Show) @@ -81,18 +81,18 @@ newTimerScheduler = liftQuasarIO do ioWorker <- askIOWorker exceptionSink <- askExceptionSink mfix \scheduler -> do - disposer <- startSchedulerThread scheduler + thread <- startSchedulerThread scheduler pure TimerScheduler { heap, activeCount, cancelledCount, - disposer, + thread, ioWorker, exceptionSink } -startSchedulerThread :: TimerScheduler -> QuasarIO Disposer -startSchedulerThread scheduler = getDisposer <$> async (schedulerThread `finally` liftIO cancelAll) +startSchedulerThread :: TimerScheduler -> QuasarIO (Async ()) +startSchedulerThread scheduler = async (schedulerThread `finally` liftIO cancelAll) where heap' :: TMVar (Heap Timer) heap' = heap scheduler diff --git a/src/Quasar/Timer/PosixTimer.hsc b/src/Quasar/Timer/PosixTimer.hsc index e578ed02bf13b13b8e2beced032a09d7f1988c63..18b42076e26d41bbdf949db08b4e9b6d39ba29a6 100644 --- a/src/Quasar/Timer/PosixTimer.hsc +++ b/src/Quasar/Timer/PosixTimer.hsc @@ -161,7 +161,7 @@ data PosixTimer = PosixTimer { } instance Resource PosixTimer where - getDisposer = disposer + getDisposer timer = [(disposer timer)] newPosixTimer :: (MonadQuasar m, MonadIO m) => ClockId -> IO () -> m PosixTimer