Skip to content

Commit e97809a

Browse files
committed
io-classes: MonadTraceSTM
Trace STM vars as they when they are committed. The internal representation of `TQueue' and `TBQueue` has changed to make the implementation simpler.
1 parent e79e99c commit e97809a

File tree

6 files changed

+560
-85
lines changed

6 files changed

+560
-85
lines changed

io-classes/src/Control/Monad/Class/MonadSTM.hs

Lines changed: 152 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
{-# LANGUAGE GADTs #-}
66
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
77
{-# LANGUAGE MultiParamTypeClasses #-}
8+
{-# LANGUAGE RankNTypes #-}
89
{-# LANGUAGE StandaloneDeriving #-}
910
{-# LANGUAGE TypeFamilies #-}
1011
{-# LANGUAGE TypeFamilyDependencies #-}
@@ -14,11 +15,14 @@
1415
module Control.Monad.Class.MonadSTM
1516
( MonadSTM (..)
1617
, MonadLabelledSTM (..)
18+
, MonadInspectSTM (..)
19+
, MonadTraceSTM (..)
1720
, LazyTVar
1821
, LazyTMVar
1922
-- * Default 'TMVar' implementation
2023
, TMVarDefault (..)
2124
, labelTMVarDefault
25+
, traceTMVarDefault
2226
, newTMVarDefault
2327
, newTMVarIODefault
2428
, newEmptyTMVarDefault
@@ -90,6 +94,7 @@ import Control.Applicative (Alternative (..))
9094
import Control.Exception
9195
import Data.Function (on)
9296
import Data.Kind (Type)
97+
import Data.Typeable (Typeable)
9398
import GHC.Stack
9499
import Numeric.Natural (Natural)
95100

@@ -238,6 +243,130 @@ class MonadSTM m
238243
default labelTBQueueIO :: TBQueue m a -> String -> m ()
239244
labelTBQueueIO = \v l -> atomically (labelTBQueue v l)
240245

246+
247+
-- | This type class is indented for 'io-sim', where one might want to access
248+
-- 'TVar' in the underlying 'ST' monad.
249+
--
250+
class ( MonadSTM m
251+
, Monad (InspectMonad m)
252+
)
253+
=> MonadInspectSTM m where
254+
type InspectMonad m :: Type -> Type
255+
inspectTVar :: proxy m -> TVar m a -> InspectMonad m a
256+
inspectTMVar :: proxy m -> TMVar m a -> InspectMonad m (Maybe a)
257+
-- TODO: inspectTQueue, inspectTBQueue
258+
259+
instance MonadInspectSTM IO where
260+
type InspectMonad IO = IO
261+
inspectTVar _ = readTVarIO
262+
-- issue #3198: tryReadTMVarIO
263+
inspectTMVar _ = atomically . tryReadTMVar
264+
265+
266+
-- | 'MonadTraceSTM' allows to trace values of stm variables when stm
267+
-- transaction is commited. This allows to verify invariants when a variable
268+
-- is commited.
269+
--
270+
class MonadInspectSTM m
271+
=> MonadTraceSTM m where
272+
-- | Construct a trace out of previous & new value of a 'TVar'. The callback
273+
-- is called whenever an stm transaction which modifies the 'TVar' is
274+
-- commited.
275+
--
276+
-- This is supported by 'IOSim' and 'IOSimPOR'; 'IO' has a trivial instance.
277+
--
278+
{-# MINIMAL traceTVar, traceTQueue, traceTBQueue #-}
279+
280+
traceTVar :: Typeable tr
281+
=> proxy m
282+
-> TVar m a
283+
-> (Maybe a -> a -> InspectMonad m tr)
284+
-- ^ callback which receives initial value or 'Nothing' (if it
285+
-- is a newly created 'TVar'), and the commited value.
286+
-> STM m ()
287+
288+
289+
traceTMVar :: Typeable tr
290+
=> proxy m
291+
-> TMVar m a
292+
-> (Maybe (Maybe a) -> (Maybe a) -> InspectMonad m tr)
293+
-> STM m ()
294+
295+
traceTQueue :: Typeable tr
296+
=> proxy m
297+
-> TQueue m a
298+
-> (Maybe [a] -> [a] -> InspectMonad m tr)
299+
-> STM m ()
300+
301+
traceTBQueue :: Typeable tr
302+
=> proxy m
303+
-> TBQueue m a
304+
-> (Maybe [a] -> [a] -> InspectMonad m tr)
305+
-> STM m ()
306+
307+
default traceTMVar :: ( TMVar m a ~ TMVarDefault m a
308+
, Typeable tr
309+
)
310+
=> proxy m
311+
-> TMVar m a
312+
-> (Maybe (Maybe a) -> (Maybe a) -> InspectMonad m tr)
313+
-> STM m ()
314+
traceTMVar = traceTMVarDefault
315+
316+
317+
traceTVarIO :: Typeable tr
318+
=> proxy m
319+
-> TVar m a
320+
-> (Maybe a -> a -> InspectMonad m tr)
321+
-> m ()
322+
323+
traceTMVarIO :: Typeable tr
324+
=> proxy m
325+
-> TMVar m a
326+
-> (Maybe (Maybe a) -> (Maybe a) -> InspectMonad m tr)
327+
-> m ()
328+
329+
traceTQueueIO :: Typeable tr
330+
=> proxy m
331+
-> TQueue m a
332+
-> (Maybe [a] -> [a] -> InspectMonad m tr)
333+
-> m ()
334+
335+
traceTBQueueIO :: Typeable tr
336+
=> proxy m
337+
-> TBQueue m a
338+
-> (Maybe [a] -> [a] -> InspectMonad m tr)
339+
-> m ()
340+
341+
default traceTVarIO :: Typeable tr
342+
=> proxy m
343+
-> TVar m a
344+
-> (Maybe a -> a -> InspectMonad m tr)
345+
-> m ()
346+
traceTVarIO = \p v f -> atomically (traceTVar p v f)
347+
348+
default traceTMVarIO :: Typeable tr
349+
=> proxy m
350+
-> TMVar m a
351+
-> (Maybe (Maybe a) -> (Maybe a) -> InspectMonad m tr)
352+
-> m ()
353+
traceTMVarIO = \p v f -> atomically (traceTMVar p v f)
354+
355+
default traceTQueueIO :: Typeable tr
356+
=> proxy m
357+
-> TQueue m a
358+
-> (Maybe [a] -> [a] -> InspectMonad m tr)
359+
-> m ()
360+
traceTQueueIO = \p v f -> atomically (traceTQueue p v f)
361+
362+
default traceTBQueueIO :: Typeable tr
363+
=> proxy m
364+
-> TBQueue m a
365+
-> (Maybe [a] -> [a] -> InspectMonad m tr)
366+
-> m ()
367+
traceTBQueueIO = \p v f -> atomically (traceTBQueue p v f)
368+
369+
241370
--
242371
-- Instance for IO uses the existing STM library implementations
243372
--
@@ -310,6 +439,19 @@ instance MonadLabelledSTM IO where
310439
labelTQueueIO = \_ _ -> return ()
311440
labelTBQueueIO = \_ _ -> return ()
312441

442+
-- | noop instance
443+
--
444+
instance MonadTraceSTM IO where
445+
traceTVar = \_ _ _ -> return ()
446+
traceTMVar = \_ _ _ -> return ()
447+
traceTQueue = \_ _ _ -> return ()
448+
traceTBQueue = \_ _ _ -> return ()
449+
450+
traceTVarIO = \_ _ _ -> return ()
451+
traceTMVarIO = \_ _ _ -> return ()
452+
traceTQueueIO = \_ _ _ -> return ()
453+
traceTBQueueIO = \_ _ _ -> return ()
454+
313455
-- | Wrapper around 'BlockedIndefinitelyOnSTM' that stores a call stack
314456
data BlockedIndefinitely = BlockedIndefinitely {
315457
blockedIndefinitelyCallStack :: CallStack
@@ -327,7 +469,7 @@ wrapBlockedIndefinitely :: HasCallStack => IO a -> IO a
327469
wrapBlockedIndefinitely = handle (throwIO . BlockedIndefinitely callStack)
328470

329471
--
330-
-- Default TMVar implementation in terms of TVars (used by sim)
472+
-- Default TMVar implementation in terms of TVars
331473
--
332474

333475
newtype TMVarDefault m a = TMVar (TVar m (Maybe a))
@@ -337,6 +479,14 @@ labelTMVarDefault
337479
=> TMVarDefault m a -> String -> STM m ()
338480
labelTMVarDefault (TMVar tvar) = labelTVar tvar
339481

482+
traceTMVarDefault
483+
:: (MonadTraceSTM m, Typeable tr)
484+
=> proxy m
485+
-> TMVarDefault m a
486+
-> (Maybe (Maybe a) -> Maybe a -> InspectMonad m tr)
487+
-> STM m ()
488+
traceTMVarDefault p (TMVar t) f = traceTVar p t f
489+
340490
newTMVarDefault :: MonadSTM m => a -> STM m (TMVarDefault m a)
341491
newTMVarDefault a = do
342492
t <- newTVar (Just a)
@@ -486,7 +636,7 @@ tryPeekTQueueDefault (TQueue read _write) = do
486636
_ -> return Nothing
487637

488638
--
489-
-- Default TBQueue implementation in terms of TVars (used by sim)
639+
-- Default TBQueue implementation in terms of TVars
490640
--
491641

492642
data TBQueueDefault m a = TBQueue

io-sim/io-sim.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ library
3030
Control.Monad.IOSim.Types
3131
other-modules: Control.Monad.IOSim.Internal,
3232
Control.Monad.IOSim.InternalTypes,
33+
Control.Monad.IOSim.STM,
3334
Control.Monad.IOSimPOR.Internal,
3435
Control.Monad.IOSimPOR.QuickCheckUtils,
3536
Control.Monad.IOSimPOR.Timeout

0 commit comments

Comments
 (0)