From 5164e9a145f749a8e806480de75b0ef3a5799110 Mon Sep 17 00:00:00 2001
From: Jens Nolte <git@queezle.net>
Date: Thu, 16 Sep 2021 23:15:01 +0200
Subject: [PATCH] Implement running ProtocolM from STM

---
 src/Quasar/Wayland/Protocol.hs      |  1 +
 src/Quasar/Wayland/Protocol/Core.hs | 28 +++++++++++++++++++---------
 2 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/src/Quasar/Wayland/Protocol.hs b/src/Quasar/Wayland/Protocol.hs
index 1d6acea..ab7705c 100644
--- a/src/Quasar/Wayland/Protocol.hs
+++ b/src/Quasar/Wayland/Protocol.hs
@@ -24,6 +24,7 @@ module Quasar.Wayland.Protocol (
 
   -- ** Low-level protocol interaction
   ProtocolM,
+  runProtocolTransaction,
   runProtocolM,
   Object,
   newObject,
diff --git a/src/Quasar/Wayland/Protocol/Core.hs b/src/Quasar/Wayland/Protocol/Core.hs
index 739ef22..bd759ac 100644
--- a/src/Quasar/Wayland/Protocol/Core.hs
+++ b/src/Quasar/Wayland/Protocol/Core.hs
@@ -23,6 +23,7 @@ module Quasar.Wayland.Protocol.Core (
   feedInput,
   setException,
   takeOutbox,
+  runProtocolTransaction,
   runProtocolM,
 
   -- * Low-level protocol interaction
@@ -425,11 +426,12 @@ initializeProtocol wlDisplayCallback initializationAction = do
     wlDisplay :: Object s wl_display
     wlDisplay = Object 1 wlDisplayCallback
 
--- | Entry point to run a protocol action, effectively an 'atomically' with correct error handling.
+-- | Run a protocol action in 'IO'. If an exception occurs, it is stored as a protocol failure and is then
+-- re-thrown.
 --
--- Throws an exception, when the protocol reaches or is in a failed (/error) state.
-runProtocolM :: (MonadIO m, MonadThrow m) => ProtocolHandle s -> ProtocolM s a -> m a
-runProtocolM (ProtocolHandle stateVar) action = do
+-- Throws an exception, if the protocol is already in a failed state.
+runProtocolTransaction :: MonadIO m => ProtocolHandle s -> ProtocolM s a -> m a
+runProtocolTransaction (ProtocolHandle stateVar) action = do
   result <- liftIO $ atomically do
     readTVar stateVar >>= \case
       -- Protocol is already in a failed state
@@ -444,25 +446,33 @@ runProtocolM (ProtocolHandle stateVar) action = do
           Right result -> do
             pure (Right result)
   -- Transaction is committed, rethrow exception if the action failed
-  either throwM pure result
+  either (liftIO . throwM) pure result
 
 
+-- | Run a 'ProtocolM'-action inside 'STM'.
+--
+-- Exceptions are not handled and reset the transaction (as usual with STM).
+--
+-- Throws an exception, if the protocol is already in a failed state.
+runProtocolM :: ProtocolHandle s -> ProtocolM s a -> STM a
+runProtocolM protocol action = either throwM (runReaderT action) =<< readTVar protocol.stateVar
+
 
 -- | Feed the protocol newly received data.
 feedInput :: (IsSide s, MonadIO m, MonadThrow m) => ProtocolHandle s -> ByteString -> m ()
-feedInput protocol bytes = runProtocolM protocol do
-  -- Exposing MonadIO instead of STM to the outside and using `runProtocolM` here enforces correct exception handling.
+feedInput protocol bytes = runProtocolTransaction protocol do
+  -- Exposing MonadIO instead of STM to the outside and using `runProtocolTransaction` here enforces correct exception handling.
   modifyProtocolVar (.bytesReceivedVar) (+ fromIntegral (BS.length bytes))
   modifyProtocolVar (.inboxDecoderVar) (`pushChunk` bytes)
   receiveMessages
 
 -- | Set the protocol to a failed state, e.g. when the socket closed unexpectedly.
 setException :: (Exception e, MonadIO m, MonadThrow m) => ProtocolHandle s -> e -> m ()
-setException protocol ex = runProtocolM protocol $ throwM ex
+setException protocol ex = runProtocolTransaction protocol $ throwM ex
 
 -- | Take data that has to be sent. Blocks until data is available.
 takeOutbox :: (MonadIO m, MonadThrow m) => ProtocolHandle s -> m (BSL.ByteString)
-takeOutbox protocol = runProtocolM protocol do
+takeOutbox protocol = runProtocolTransaction protocol do
   mOutboxData <- stateProtocolVar (.outboxVar) (\mOutboxData -> (mOutboxData, Nothing))
   outboxData <- maybe (lift retry) pure mOutboxData
   let sendData = runPut outboxData
-- 
GitLab