From 3d7860534fe2882152dd14575af51c57846f8456 Mon Sep 17 00:00:00 2001
From: Jens Nolte <git@queezle.net>
Date: Thu, 10 Feb 2022 19:58:01 +0100
Subject: [PATCH] Add TIOWorker

Co-authored-by: Jan Beinke <git@janbeinke.com>
---
 quasar.cabal                  |  1 +
 src/Quasar/Async/STMHelper.hs | 47 +++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+)
 create mode 100644 src/Quasar/Async/STMHelper.hs

diff --git a/quasar.cabal b/quasar.cabal
index ca9695b..afea5a0 100644
--- a/quasar.cabal
+++ b/quasar.cabal
@@ -87,6 +87,7 @@ library
     Quasar
     Quasar.Async
     Quasar.Async.Unmanaged
+    Quasar.Async.STMHelper
     Quasar.Awaitable
     Quasar.Disposable
     Quasar.Exceptions
diff --git a/src/Quasar/Async/STMHelper.hs b/src/Quasar/Async/STMHelper.hs
new file mode 100644
index 0000000..11dccad
--- /dev/null
+++ b/src/Quasar/Async/STMHelper.hs
@@ -0,0 +1,47 @@
+module Quasar.Async.STMHelper (
+  TIOWorker,
+  newTIOWorker,
+  startTrivialIO,
+  startTrivialIO_,
+) where
+
+import Control.Concurrent (forkIO)
+import Control.Concurrent.STM
+import Control.Exception (BlockedIndefinitelyOnSTM)
+import Control.Monad.Catch
+import Quasar.Awaitable
+import Quasar.Exceptions
+import Quasar.Prelude
+
+
+newtype TIOWorker = TIOWorker (TMVar (IO ()))
+
+
+startTrivialIO :: forall a. TIOWorker -> ExceptionChannel -> IO a -> STM (Awaitable a)
+startTrivialIO (TIOWorker jobVar) exChan fn = do
+  resultVar <- newAsyncVarSTM
+  putTMVar jobVar $ job resultVar
+  pure $ toAwaitable resultVar
+  where
+    job :: AsyncVar a -> IO ()
+    job resultVar = do
+      try fn >>= \case
+        Left ex -> do
+          atomically $ throwToExceptionChannel exChan ex
+          failAsyncVar_ resultVar $ toException $ AsyncException ex
+        Right result -> putAsyncVar_ resultVar result
+
+startTrivialIO_ :: forall a. TIOWorker -> ExceptionChannel -> IO a -> STM ()
+startTrivialIO_ x y z = void $ startTrivialIO x y z
+
+
+newTIOWorker :: IO TIOWorker
+newTIOWorker = do
+  jobVar <- newEmptyTMVarIO
+  void $ forkIO $
+    handle
+      -- Relies on garbage collection to remove the thread when it is no longer needed
+      (\(_ :: BlockedIndefinitelyOnSTM) -> pure ())
+      (forever $ join $ atomically $ takeTMVar jobVar)
+    
+  pure $ TIOWorker jobVar
-- 
GitLab