reactive-banana-1.3.2.0: src/Reactive/Banana/Prim/Mid/Evaluation.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE NamedFieldPuns #-}
{-----------------------------------------------------------------------------
reactive-banana
------------------------------------------------------------------------------}
module Reactive.Banana.Prim.Mid.Evaluation
( step
, applyDependencyChanges
) where
import Control.Monad
( join )
import Control.Monad.IO.Class
( liftIO )
import qualified Reactive.Banana.Prim.Low.GraphGC as GraphGC
import qualified Reactive.Banana.Prim.Low.OrderedBag as OB
import qualified Reactive.Banana.Prim.Low.Ref as Ref
import Reactive.Banana.Prim.Mid.Plumbing
import Reactive.Banana.Prim.Mid.Types
{-----------------------------------------------------------------------------
Evaluation step
------------------------------------------------------------------------------}
-- | Evaluate all the pulses in the graph,
-- Rebuild the graph as necessary and update the latch values.
step :: Inputs -> Step
step (inputs,pulses)
Network{ nTime = time1
, nOutputs = outputs1
, nAlwaysP = alwaysP
, nGraphGC
}
= do
-- evaluate pulses
((_, (latchUpdates, outputs)), dependencyChanges, os)
<- runBuildIO (time1, alwaysP)
$ runEvalP pulses
$ evaluatePulses inputs nGraphGC
doit latchUpdates -- update latch values from pulses
applyDependencyChanges dependencyChanges -- rearrange graph topology
nGraphGC
GraphGC.removeGarbage nGraphGC -- remove unreachable pulses
let actions :: [(Output, EvalO)]
actions = OB.inOrder outputs outputs1 -- EvalO actions in proper order
state2 :: Network
!state2 = Network
{ nTime = next time1
, nOutputs = OB.inserts outputs1 os
, nAlwaysP = alwaysP
, nGraphGC
}
return (runEvalOs $ map snd actions, state2)
runEvalOs :: [EvalO] -> IO ()
runEvalOs = mapM_ join
{-----------------------------------------------------------------------------
Dependency changes
------------------------------------------------------------------------------}
-- | Apply all dependency changes to the 'GraphGC'.
applyDependencyChanges :: DependencyChanges -> Dependencies -> IO ()
applyDependencyChanges changes g = do
sequence_ [applyDependencyChange c g | c@(InsertEdge _ _) <- changes]
sequence_ [applyDependencyChange c g | c@(ChangeParentTo _ _) <- changes]
applyDependencyChange
:: DependencyChange SomeNode SomeNode -> Dependencies -> IO ()
applyDependencyChange (InsertEdge parent child) g =
GraphGC.insertEdge (parent, child) g
applyDependencyChange (ChangeParentTo child parent) g = do
GraphGC.clearPredecessors child g
GraphGC.insertEdge (parent, child) g
{-----------------------------------------------------------------------------
Traversal in dependency order
------------------------------------------------------------------------------}
-- | Update all pulses in the graph, starting from a given set of nodes
evaluatePulses :: [SomeNode] -> Dependencies -> EvalP ()
evaluatePulses inputs g = do
action <- liftIO $ GraphGC.walkSuccessors_ inputs evaluateWeakNode g
action
evaluateWeakNode :: Ref.WeakRef SomeNodeD -> EvalP GraphGC.Step
evaluateWeakNode w = do
mnode <- liftIO $ Ref.deRefWeak w
case mnode of
Nothing -> pure GraphGC.Stop
Just node -> evaluateNode node
-- | Recalculate a given node and return all children nodes
-- that need to evaluated subsequently.
evaluateNode :: SomeNode -> EvalP GraphGC.Step
evaluateNode someNode = do
node <- Ref.read someNode
case node of
P PulseD{_evalP,_keyP} -> {-# SCC evaluateNodeP #-} do
ma <- _evalP
writePulseP _keyP ma
pure $ case ma of
Nothing -> GraphGC.Stop
Just _ -> GraphGC.Next
L lw -> {-# SCC evaluateLatchWrite #-} do
evaluateLatchWrite lw
pure GraphGC.Stop
O o -> {-# SCC evaluateNodeO #-} do
m <- _evalO o -- calculate output action
rememberOutput (someNode,m)
pure GraphGC.Stop
evaluateLatchWrite :: LatchWriteD -> EvalP ()
evaluateLatchWrite LatchWriteD{_evalLW,_latchLW} = do
time <- askTime
mlatch <- liftIO $ Ref.deRefWeak _latchLW -- retrieve destination latch
case mlatch of
Nothing -> pure ()
Just latch -> do
a <- _evalLW -- calculate new latch value
-- liftIO $ Strict.evaluate a -- see Note [LatchStrictness]
rememberLatchUpdate $ -- schedule value to be set later
Ref.modify' latch $ \l ->
a `seq` l { _seenL = time, _valueL = a }