reactive-banana-1.3.1.0: src/Reactive/Banana/Prim/Low/Evaluation.hs
{-----------------------------------------------------------------------------
reactive-banana
------------------------------------------------------------------------------}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE RecordWildCards #-}
module Reactive.Banana.Prim.Low.Evaluation (
step
) where
import Control.Monad ( join )
import Control.Monad.IO.Class
import qualified Control.Monad.Trans.RWSIO as RWS
import qualified Data.PQueue.Prio.Min as Q
import qualified Data.Vault.Lazy as Lazy
import System.Mem.Weak
import qualified Reactive.Banana.Prim.Low.OrderedBag as OB
import Reactive.Banana.Prim.Low.Plumbing
import Reactive.Banana.Prim.Low.Types
import Reactive.Banana.Prim.Low.Util
type Queue = Q.MinPQueue Level
{-----------------------------------------------------------------------------
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
}
= do
-- evaluate pulses
((_, (latchUpdates, outputs)), topologyUpdates, os)
<- runBuildIO (time1, alwaysP)
$ runEvalP pulses
$ evaluatePulses inputs
doit latchUpdates -- update latch values from pulses
doit topologyUpdates -- rearrange graph topology
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
}
return (runEvalOs $ map snd actions, state2)
runEvalOs :: [EvalO] -> IO ()
runEvalOs = mapM_ join
{-----------------------------------------------------------------------------
Traversal in dependency order
------------------------------------------------------------------------------}
-- | Update all pulses in the graph, starting from a given set of nodes
evaluatePulses :: [SomeNode] -> EvalP ()
evaluatePulses roots = wrapEvalP $ \r -> go r =<< insertNodes r roots Q.empty
where
go :: RWS.Tuple BuildR (EvalPW, BuildW) Lazy.Vault -> Queue SomeNode -> IO ()
go r q =
case ({-# SCC minView #-} Q.minView q) of
Nothing -> return ()
Just (node, q) -> do
children <- unwrapEvalP r (evaluateNode node)
q <- insertNodes r children q
go r q
-- | Recalculate a given node and return all children nodes
-- that need to evaluated subsequently.
evaluateNode :: SomeNode -> EvalP [SomeNode]
evaluateNode (P p) = {-# SCC evaluateNodeP #-} do
Pulse{..} <- readRef p
ma <- _evalP
writePulseP _keyP ma
case ma of
Nothing -> return []
Just _ -> liftIO $ deRefWeaks _childrenP
evaluateNode (L lw) = {-# SCC evaluateNodeL #-} do
time <- askTime
LatchWrite{..} <- readRef lw
mlatch <- liftIO $ deRefWeak _latchLW -- retrieve destination latch
case mlatch of
Nothing -> return ()
Just latch -> do
a <- _evalLW -- calculate new latch value
-- liftIO $ Strict.evaluate a -- see Note [LatchStrictness]
rememberLatchUpdate $ -- schedule value to be set later
modify' latch $ \l ->
a `seq` l { _seenL = time, _valueL = a }
return []
evaluateNode (O o) = {-# SCC evaluateNodeO #-} do
debug "evaluateNode O"
Output{..} <- readRef o
m <- _evalO -- calculate output action
rememberOutput (o,m)
return []
-- | Insert nodes into the queue
insertNodes :: RWS.Tuple BuildR (EvalPW, BuildW) Lazy.Vault -> [SomeNode] -> Queue SomeNode -> IO (Queue SomeNode)
insertNodes (RWS.Tuple (time,_) _ _) = go
where
go :: [SomeNode] -> Queue SomeNode -> IO (Queue SomeNode)
go [] q = return q
go (node@(P p):xs) q = do
Pulse{..} <- readRef p
if time <= _seenP
then go xs q -- pulse has already been put into the queue once
else do -- pulse needs to be scheduled for evaluation
put p $! (let p = Pulse{..} in p { _seenP = time })
go xs $! Q.insert _levelP node q
go (node:xs) q = go xs $! Q.insert ground node q
-- O and L nodes have only one parent, so
-- we can insert them at an arbitrary level