From a46be5d9a09a681a5a4b2e8c6d8186572bb7b31d Mon Sep 17 00:00:00 2001
From: Jens Nolte <git@queezle.net>
Date: Sat, 29 Jan 2022 23:42:35 +0100
Subject: [PATCH] Add Resources module for simplified resource management

---
 quasar.cabal            |   1 +
 src/Quasar/Resources.hs | 113 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 114 insertions(+)
 create mode 100644 src/Quasar/Resources.hs

diff --git a/quasar.cabal b/quasar.cabal
index 28a8fc6..ca9695b 100644
--- a/quasar.cabal
+++ b/quasar.cabal
@@ -97,6 +97,7 @@ library
     Quasar.Prelude
     Quasar.PreludeExtras
     Quasar.ResourceManager
+    Quasar.Resources
     Quasar.Subscribable
     Quasar.Timer
     Quasar.Timer.PosixTimer
diff --git a/src/Quasar/Resources.hs b/src/Quasar/Resources.hs
new file mode 100644
index 0000000..25ce6fd
--- /dev/null
+++ b/src/Quasar/Resources.hs
@@ -0,0 +1,113 @@
+module Quasar.Resources (
+  Resource(..),
+  Disposer,
+  ResourceManager,
+  dispose,
+  disposeEventuallySTM,
+  disposeEventuallySTM_,
+) where
+
+
+import Control.Concurrent.STM
+import Control.Monad.Catch
+import Quasar.Awaitable
+import Quasar.Exceptions
+import Quasar.Prelude
+import Quasar.Utils.TOnce
+
+
+class Resource a where
+  getDisposer :: a -> Disposer
+
+
+type DisposerState = TOnce DisposeFn (Awaitable ())
+
+data Disposer
+  = FnDisposer ExceptionChannel DisposerState Finalizers
+  | ResourceManagerDisposer ResourceManager
+
+data DisposeFn
+  = IODisposeFn (IO ())
+  | STMDisposeFn (STM ())
+
+newDisposer :: ExceptionChannel -> IO () -> STM Disposer
+newDisposer exChan disposeFn = newFnDisposer exChan (IODisposeFn disposeFn)
+
+newSTMDisposer :: ExceptionChannel -> STM () -> STM Disposer
+newSTMDisposer exChan disposeFn = newFnDisposer exChan (STMDisposeFn disposeFn)
+
+newFnDisposer :: ExceptionChannel -> DisposeFn -> STM Disposer
+newFnDisposer exChan fn =
+  FnDisposer exChan <$> newTOnce fn <*> newFinalizersSTM
+
+
+dispose :: (MonadIO m, Resource r) => r -> m ()
+dispose resource = liftIO $ await =<< atomically (disposeEventuallySTM resource)
+
+disposeEventuallySTM :: Resource r => r -> STM (Awaitable ())
+disposeEventuallySTM resource =
+  case getDisposer resource of
+    FnDisposer channel state finalizers -> do
+      beginDispose channel state finalizers
+    ResourceManagerDisposer resourceManager ->
+      beginDisposeResourceManager resourceManager
+
+disposeEventuallySTM_ :: Resource r => r -> STM ()
+disposeEventuallySTM_ resource = void $ disposeEventuallySTM resource
+
+
+isDisposed :: Resource a => a -> Awaitable ()
+isDisposed resource =
+  case getDisposer resource of
+    FnDisposer _ state _ -> join (toAwaitable state)
+    ResourceManagerDisposer _resourceManager -> undefined -- resource manager
+
+
+beginDispose :: ExceptionChannel -> DisposerState -> Finalizers -> STM (Awaitable ())
+beginDispose channel disposeState finalizers =
+  mapFinalizeTOnce disposeState startDisposeFn
+  where
+    startDisposeFn :: DisposeFn -> STM (Awaitable ())
+    startDisposeFn = undefined -- launch dispose thread
+
+
+
+data ResourceManager = ResourceManager
+
+beginDisposeResourceManager :: ResourceManager -> STM (Awaitable ())
+beginDisposeResourceManager = undefined -- resource manager
+
+
+
+data DisposeResult
+  = DisposeResultDisposed
+  | DisposeResultAwait (Awaitable ())
+  | DisposeResultResourceManager ResourceManagerResult
+
+data ResourceManagerResult = ResourceManagerResult Unique (Awaitable [ResourceManagerResult])
+
+
+
+-- * Implementation internals
+
+newtype Finalizers = Finalizers (TMVar [STM ()])
+
+newFinalizers :: IO Finalizers
+newFinalizers = Finalizers <$> newTMVarIO []
+
+newFinalizersSTM :: STM Finalizers
+newFinalizersSTM = Finalizers <$> newTMVar []
+
+defaultRegisterFinalizer :: Finalizers -> STM () -> STM Bool
+defaultRegisterFinalizer (Finalizers finalizerVar) finalizer =
+  tryTakeTMVar finalizerVar >>= \case
+    Just finalizers -> do
+      putTMVar finalizerVar (finalizer : finalizers)
+      pure True
+    Nothing -> pure False
+
+defaultRunFinalizers :: Finalizers -> STM ()
+defaultRunFinalizers (Finalizers finalizerVar) = do
+  tryTakeTMVar finalizerVar >>= \case
+    Just finalizers -> sequence_ finalizers
+    Nothing -> throwM $ userError "defaultRunFinalizers was called multiple times (it must only be run once)"
-- 
GitLab