From 7d1e4826ae45a2663bb981eda6ba2741aa8519df Mon Sep 17 00:00:00 2001
From: Jens Nolte <git@queezle.net>
Date: Tue, 7 Sep 2021 19:43:45 +0200
Subject: [PATCH] Improve connection error handling

---
 src/Quasar/Wayland/Connection.hs | 25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/src/Quasar/Wayland/Connection.hs b/src/Quasar/Wayland/Connection.hs
index 2ba630e..ebb020b 100644
--- a/src/Quasar/Wayland/Connection.hs
+++ b/src/Quasar/Wayland/Connection.hs
@@ -30,6 +30,10 @@ instance IsResourceManager (WaylandConnection s) where
 instance IsDisposable (WaylandConnection s) where
   toDisposable connection = toDisposable connection.resourceManager
 
+data SocketClosed = SocketClosed
+  deriving stock Show
+  deriving anyclass Exception
+
 newWaylandConnection :: forall s m. MonadResourceManager m => Callback s STM I_wl_display -> Socket -> m (WaylandConnection s)
 newWaylandConnection wlDisplayCallback socket = do
   protocolStateVar <- liftIO $ newTVarIO $ initialProtocolState wlDisplayCallback
@@ -48,8 +52,8 @@ newWaylandConnection wlDisplayCallback socket = do
     registerDisposeAction $ closeConnection connection
 
     runUnlimitedAsync do
-      async $ liftIO $ waylandConnectionSendThread connection `catchAll` \ex -> traceIO (displayException ex) >> void (dispose resourceManager)
-      async $ liftIO $ waylandConnectionReceiveThread connection `catchAll` \ex -> traceIO (displayException ex) >> void (dispose resourceManager)
+      connectionThread connection $ sendThread connection
+      connectionThread connection $ receiveThread connection
 
     -- HACK to send first message (queued internally)
     stepProtocol connection $ feedInput ""
@@ -68,21 +72,26 @@ stepProtocol connection step = liftIO do
     Left ex -> throwM (ex :: SomeException)
     Right result -> pure result
 
+connectionThread :: MonadAsync m => WaylandConnection s -> IO () -> m ()
+connectionThread connection work = async_ $ liftIO $ work `catches` [ignoreCancelTask, handleAll]
+  where
+    ignoreCancelTask = Handler (throwM :: CancelTask -> IO a)
+    handleAll = Handler (\(ex :: SomeException) -> traceIO (displayException ex) >> void (dispose connection))
 
-waylandConnectionSendThread :: WaylandConnection s -> IO ()
-waylandConnectionSendThread connection = forever do
+sendThread :: WaylandConnection s -> IO ()
+sendThread connection = forever do
   bytes <- atomically $ takeTMVar connection.outboxVar
 
-  traceIO $ "Sending data: " <> show (BSL.length bytes) <> " bytes"
+  traceIO $ "Sending " <> show (BSL.length bytes) <> " bytes"
   SocketL.sendAll connection.socket bytes
 
 
-waylandConnectionReceiveThread :: WaylandConnection s -> IO ()
-waylandConnectionReceiveThread connection = forever do
+receiveThread :: WaylandConnection s -> IO ()
+receiveThread connection = forever do
   bytes <- Socket.recv connection.socket 4096
 
   when (BS.length bytes == 0) do
-    fail "Socket is closed"
+    throwM SocketClosed
 
   traceIO $ "Received " <> show (BS.length bytes) <> " bytes"
 
-- 
GitLab