Skip to content
This repository was archived by the owner on Aug 18, 2020. It is now read-only.

Commit d7c3a0f

Browse files
committed
withWalletWorker: use Async and precise exception type
1 parent 23732ef commit d7c3a0f

File tree

1 file changed

+23
-22
lines changed

1 file changed

+23
-22
lines changed

wallet-new/src/Cardano/Wallet/Kernel/Actions.hs

+23-22
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module Cardano.Wallet.Kernel.Actions
33
( WalletAction(..)
44
, WalletActionInterp(..)
55
, withWalletWorker
6+
, Err_WalletWorkerExpired(..)
67
, interp
78
, interpList
89
, WalletWorkerState
@@ -11,7 +12,7 @@ module Cardano.Wallet.Kernel.Actions
1112
, isValidState
1213
) where
1314

14-
import Control.Concurrent (forkFinally)
15+
import qualified Control.Concurrent.Async as Async
1516
import qualified Control.Concurrent.STM as STM
1617
import qualified Control.Exception.Safe as Ex
1718
import Control.Monad.Morph (MFunctor(hoist))
@@ -150,51 +151,51 @@ initialWorkerState = WalletWorkerState
150151
, _lengthPendingBlocks = 0
151152
}
152153

154+
-- | Thrown by 'withWalletWorker''s continuation in case it's used outside of
155+
-- its intended scope.
156+
data Err_WalletWorkerExpired = Err_WalletWorkerExpired deriving (Show)
157+
instance Ex.Exception Err_WalletWorkerExpired
153158

154159
-- | Start a wallet worker in backround who will react to input provided via the
155160
-- 'STM' function, in FIFO order.
156161
--
157162
-- After the given continuation returns (successfully or due to some exception),
158163
-- the worker will continue processing any pending input before returning,
159-
-- re-throwing the continuation's exception if any.
164+
-- re-throwing the continuation's exception if any. Async exceptions from any
165+
-- source will always be prioritized.
160166
--
161167
-- Usage of the obtained 'STM' action after the given continuation has returned
162-
-- will fail with an exception.
168+
-- is not possible. It will throw 'Err_WalletWorkerExpired'.
163169
withWalletWorker
164170
:: (MonadIO m, Ex.MonadMask m)
165171
=> WalletActionInterp IO a
166172
-> ((WalletAction a -> STM ()) -> m b)
167173
-> m b
168174
withWalletWorker wai k = do
169-
-- 'mDone' is full if the worker finished.
170-
mDone :: MVar (Either Ex.SomeException ()) <- liftIO newEmptyMVar
171-
-- 'tqWA' keeps items to be processed by the worker.
175+
-- 'tqWA' keeps items to be processed by the worker in FIFO order.
172176
tqWA :: STM.TQueue (WalletAction a) <- liftIO STM.newTQueueIO
173177
-- 'tvOpen' is 'True' as long as 'tqWA' can receive new input.
174178
tvOpen :: STM.TVar Bool <- liftIO (STM.newTVarIO True)
175-
-- 'getWA' returns the next action to be processed. This function blocks
179+
-- 'getWA' returns the next action to be processed. This function retries
176180
-- unless 'tvOpen' is 'False', in which case 'Shutdown' is returned.
177181
let getWA :: STM (WalletAction a)
178182
getWA = STM.tryReadTQueue tqWA >>= \case
179183
Just wa -> pure wa
180184
Nothing -> STM.readTVar tvOpen >>= \case
181185
False -> pure Shutdown
182186
True -> STM.retry
183-
-- 'pushWA' adds an action to be executed by the worker, in FIFO order. It
184-
-- will throw 'BlockedIndefinitelyOnSTM' if used after `k` returns.
185-
let pushWA :: WalletAction a -> STM ()
186-
pushWA = \wa -> do STM.check =<< STM.readTVar tvOpen
187-
STM.writeTQueue tqWA wa
188-
liftIO $ void $ forkFinally
189-
(walletWorker wai (STM.atomically getWA))
190-
(putMVar mDone)
191-
Ex.finally
192-
(k pushWA)
193-
(liftIO $ do
194-
-- Prevent new input.
195-
STM.atomically (STM.writeTVar tvOpen False)
196-
-- Wait for the worker to finish.
197-
either Ex.throwM pure =<< takeMVar mDone)
187+
Ex.bracket
188+
(liftIO (Async.async (walletWorker wai (STM.atomically getWA))))
189+
(\a1 -> liftIO $ do
190+
-- Prevent new input.
191+
STM.atomically (STM.writeTVar tvOpen False)
192+
-- Wait for the worker to finish, re-throwing any exceptions from it.
193+
Async.wait a1)
194+
(\_ -> k $ \wa -> do
195+
-- Add a WalletAction to the queue, unless it's been closed already.
196+
STM.readTVar tvOpen >>= \case
197+
True -> STM.writeTQueue tqWA wa
198+
False -> Ex.throwM Err_WalletWorkerExpired)
198199

199200
-- | Check if this is the initial worker state.
200201
isInitialState :: Eq b => WalletWorkerState b -> Bool

0 commit comments

Comments
 (0)