module Reflex.Spider.Internal where
import qualified Reflex.Class as R
import qualified Reflex.Host.Class as R
import Data.IORef
import System.Mem.Weak
import Data.Foldable
import Data.Traversable
import Control.Monad hiding (mapM, mapM_, forM_, forM, sequence)
import Control.Monad.Identity hiding (mapM, mapM_, forM_, forM, sequence)
import Control.Monad.Reader hiding (mapM, mapM_, forM_, forM, sequence)
import GHC.Exts
import Control.Applicative
import Data.Dependent.Map (DMap, DSum (..))
import qualified Data.Dependent.Map as DMap
import Data.GADT.Compare
import Data.Maybe
import Data.IntMap (IntMap)
import qualified Data.IntMap as IntMap
import Control.Monad.Ref
import Control.Monad.Exception
import Data.Monoid ((<>))
import Data.Coerce
import System.IO.Unsafe
import Unsafe.Coerce
import Control.Monad.Primitive
import Prelude hiding (mapM, mapM_, any, sequence, concat)
debugPropagate :: Bool
debugInvalidateHeight :: Bool
#ifdef DEBUG
#define DEBUG_NODEIDS
debugPropagate = True
debugInvalidateHeight = True
class HasNodeId a where
getNodeId :: a -> Int
instance HasNodeId (Hold a) where
getNodeId = holdNodeId
instance HasNodeId (PushSubscribed a b) where
getNodeId = pushSubscribedNodeId
instance HasNodeId (SwitchSubscribed a) where
getNodeId = switchSubscribedNodeId
instance HasNodeId (MergeSubscribed a) where
getNodeId = mergeSubscribedNodeId
instance HasNodeId (FanSubscribed a) where
getNodeId = fanSubscribedNodeId
instance HasNodeId (CoincidenceSubscribed a) where
getNodeId = coincidenceSubscribedNodeId
instance HasNodeId (RootSubscribed a) where
getNodeId = rootSubscribedNodeId
showNodeId :: HasNodeId a => a -> String
showNodeId = ("#"<>) . show . getNodeId
#else
debugPropagate = False
debugInvalidateHeight = False
showNodeId :: a -> String
showNodeId = const ""
#endif
#ifdef DEBUG_NODEIDS
nextNodeIdRef :: IORef Int
nextNodeIdRef = unsafePerformIO $ newIORef 1
unsafeNodeId :: a -> Int
unsafeNodeId a = unsafePerformIO $ do
touch a
atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
#endif
data Hold a
= Hold { holdValue :: !(IORef a)
, holdInvalidators :: !(IORef [Weak Invalidator])
, holdSubscriber :: !(IORef Any)
, holdParent :: !(IORef Any)
#ifdef DEBUG_NODEIDS
, holdNodeId :: Int
#endif
}
data EventEnv
= EventEnv { eventEnvAssignments :: !(IORef [SomeAssignment])
, eventEnvHoldInits :: !(IORef [SomeHoldInit])
, eventEnvClears :: !(IORef [SomeMaybeIORef])
, eventEnvRootClears :: !(IORef [SomeDMapIORef])
, eventEnvCurrentHeight :: !(IORef Int)
, eventEnvCoincidenceInfos :: !(IORef [SomeCoincidenceInfo])
, eventEnvDelayedMerges :: !(IORef (IntMap [DelayedMerge]))
}
runEventM :: EventM a -> EventEnv -> IO a
runEventM = runReaderT . unEventM
askToAssignRef :: EventM (IORef [SomeAssignment])
askToAssignRef = EventM $ asks eventEnvAssignments
askHoldInitRef :: EventM (IORef [SomeHoldInit])
askHoldInitRef = EventM $ asks eventEnvHoldInits
getCurrentHeight :: EventM Int
getCurrentHeight = EventM $ do
heightRef <- asks eventEnvCurrentHeight
liftIO $ readIORef heightRef
putCurrentHeight :: Int -> EventM ()
putCurrentHeight h = EventM $ do
heightRef <- asks eventEnvCurrentHeight
liftIO $ writeIORef heightRef h
scheduleClear :: IORef (Maybe a) -> EventM ()
scheduleClear r = EventM $ do
clears <- asks eventEnvClears
liftIO $ modifyIORef' clears (SomeMaybeIORef r :)
scheduleRootClear :: IORef (DMap k Identity) -> EventM ()
scheduleRootClear r = EventM $ do
clears <- asks eventEnvRootClears
liftIO $ modifyIORef' clears (SomeDMapIORef r :)
scheduleMerge :: Int -> MergeSubscribed a -> EventM ()
scheduleMerge height subscribed = EventM $ do
delayedRef <- asks eventEnvDelayedMerges
liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) height [DelayedMerge subscribed]
emitCoincidenceInfo :: SomeCoincidenceInfo -> EventM ()
emitCoincidenceInfo sci = EventM $ do
ciRef <- asks eventEnvCoincidenceInfos
liftIO $ modifyIORef' ciRef (sci:)
hold :: a -> Event a -> EventM (Behavior a)
hold v0 e = do
holdInitRef <- askHoldInitRef
liftIO $ do
valRef <- newIORef v0
invsRef <- newIORef []
parentRef <- newIORef $ error "hold not yet initialized (parent)"
subscriberRef <- newIORef $ error "hold not yet initialized (subscriber)"
let h = Hold
{ holdValue = valRef
, holdInvalidators = invsRef
, holdSubscriber = subscriberRef
, holdParent = parentRef
#ifdef DEBUG_NODEIDS
, holdNodeId = unsafeNodeId (v0, e)
#endif
}
s <- newSubscriberHold h
writeIORef subscriberRef $ unsafeCoerce s
modifyIORef' holdInitRef (SomeHoldInit e h :)
return $ BehaviorHold h
subscribeHold :: Event a -> Hold a -> EventM ()
subscribeHold e h = do
toAssignRef <- askToAssignRef
!s <- liftIO $ liftM unsafeCoerce $ readIORef $ holdSubscriber h
ws <- liftIO $ mkWeakPtrWithDebug s "holdSubscriber"
subd <- subscribe e $ WeakSubscriberSimple ws
liftIO $ writeIORef (holdParent h) $ unsafeCoerce subd
occ <- liftIO $ getEventSubscribedOcc subd
case occ of
Nothing -> return ()
Just o -> liftIO $ modifyIORef' toAssignRef (SomeAssignment h o :)
newtype BehaviorM a = BehaviorM { unBehaviorM :: ReaderT (Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed])) IO a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
data BehaviorSubscribed a
= BehaviorSubscribedHold (Hold a)
| BehaviorSubscribedPull (PullSubscribed a)
data SomeBehaviorSubscribed = forall a. SomeBehaviorSubscribed (BehaviorSubscribed a)
data PullSubscribed a
= PullSubscribed { pullSubscribedValue :: !a
, pullSubscribedInvalidators :: !(IORef [Weak Invalidator])
, pullSubscribedOwnInvalidator :: !Invalidator
, pullSubscribedParents :: ![SomeBehaviorSubscribed]
}
data Pull a
= Pull { pullValue :: !(IORef (Maybe (PullSubscribed a)))
, pullCompute :: !(BehaviorM a)
}
data Invalidator
= forall a. InvalidatorPull (Pull a)
| forall a. InvalidatorSwitch (SwitchSubscribed a)
data RootSubscribed a
= RootSubscribed { rootSubscribedSubscribers :: !(IORef [WeakSubscriber a])
, rootSubscribedOccurrence :: !(IO (Maybe a))
}
data Root (k :: * -> *)
= Root { rootOccurrence :: !(IORef (DMap k Identity))
, rootSubscribed :: !(IORef (DMap k RootSubscribed))
, rootInit :: !(forall a. k a -> RootTrigger a -> IO (IO ()))
}
data SomeHoldInit = forall a. SomeHoldInit (Event a) (Hold a)
newtype EventM a = EventM { unEventM :: ReaderT EventEnv IO a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
data PushSubscribed a b
= PushSubscribed { pushSubscribedOccurrence :: !(IORef (Maybe b))
, pushSubscribedHeight :: !(IORef Int)
, pushSubscribedSubscribers :: !(IORef [WeakSubscriber b])
, pushSubscribedSelf :: !(Subscriber a)
, pushSubscribedParent :: !(EventSubscribed a)
#ifdef DEBUG_NODEIDS
, pushSubscribedNodeId :: Int
#endif
}
data Push a b
= Push { pushCompute :: !(a -> EventM (Maybe b))
, pushParent :: !(Event a)
, pushSubscribed :: !(IORef (Maybe (PushSubscribed a b)))
}
data MergeSubscribed k
= MergeSubscribed { mergeSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
, mergeSubscribedAccum :: !(IORef (DMap k Identity))
, mergeSubscribedHeight :: !(IORef Int)
, mergeSubscribedSubscribers :: !(IORef [WeakSubscriber (DMap k Identity)])
, mergeSubscribedSelf :: !Any
, mergeSubscribedParents :: !(DMap k EventSubscribed)
#ifdef DEBUG_NODEIDS
, mergeSubscribedNodeId :: Int
#endif
}
data Merge k
= Merge { mergeParents :: !(DMap k Event)
, mergeSubscribed :: !(IORef (Maybe (MergeSubscribed k)))
}
data FanSubscriberKey k a where
FanSubscriberKey :: k a -> FanSubscriberKey k [WeakSubscriber a]
instance GEq k => GEq (FanSubscriberKey k) where
geq (FanSubscriberKey a) (FanSubscriberKey b) = case geq a b of
Nothing -> Nothing
Just Refl -> Just Refl
instance GCompare k => GCompare (FanSubscriberKey k) where
gcompare (FanSubscriberKey a) (FanSubscriberKey b) = case gcompare a b of
GLT -> GLT
GEQ -> GEQ
GGT -> GGT
data FanSubscribed k
= FanSubscribed { fanSubscribedSubscribers :: !(IORef (DMap (FanSubscriberKey k) Identity))
, fanSubscribedParent :: !(EventSubscribed (DMap k Identity))
, fanSubscribedSelf :: (Subscriber (DMap k Identity))
#ifdef DEBUG_NODEIDS
, fanSubscribedNodeId :: Int
#endif
}
data Fan k
= Fan { fanParent :: !(Event (DMap k Identity))
, fanSubscribed :: !(IORef (Maybe (FanSubscribed k)))
}
data SwitchSubscribed a
= SwitchSubscribed { switchSubscribedOccurrence :: !(IORef (Maybe a))
, switchSubscribedHeight :: !(IORef Int)
, switchSubscribedSubscribers :: !(IORef [WeakSubscriber a])
, switchSubscribedSelf :: (Subscriber a)
, switchSubscribedSelfWeak :: !(IORef (Weak (Subscriber a)))
, switchSubscribedOwnInvalidator :: Invalidator
, switchSubscribedOwnWeakInvalidator :: !(IORef (Weak Invalidator))
, switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed])
, switchSubscribedParent :: !(Behavior (Event a))
, switchSubscribedCurrentParent :: !(IORef (EventSubscribed a))
#ifdef DEBUG_NODEIDS
, switchSubscribedNodeId :: Int
#endif
}
data Switch a
= Switch { switchParent :: !(Behavior (Event a))
, switchSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
}
data CoincidenceSubscribed a
= CoincidenceSubscribed { coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
, coincidenceSubscribedSubscribers :: !(IORef [WeakSubscriber a])
, coincidenceSubscribedHeight :: !(IORef Int)
, coincidenceSubscribedOuter :: (Subscriber (Event a))
, coincidenceSubscribedOuterParent :: !(EventSubscribed (Event a))
, coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed a)))
#ifdef DEBUG_NODEIDS
, coincidenceSubscribedNodeId :: Int
#endif
}
data Coincidence a
= Coincidence { coincidenceParent :: !(Event (Event a))
, coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
}
data Box a = Box { unBox :: a }
data WeakSubscriber a
= forall k. GCompare k => WeakSubscriberMerge !(k a) !(Weak (Box (MergeSubscribed k)))
| WeakSubscriberSimple !(Weak (Subscriber a))
showWeakSubscriberType :: WeakSubscriber a -> String
showWeakSubscriberType = \case
WeakSubscriberMerge _ _ -> "WeakSubscriberMerge"
WeakSubscriberSimple _ -> "WeakSubscriberSimple"
deRefWeakSubscriber :: WeakSubscriber a -> IO (Maybe (Subscriber a))
deRefWeakSubscriber ws = case ws of
WeakSubscriberSimple w -> deRefWeak w
WeakSubscriberMerge k w -> liftM (fmap $ SubscriberMerge k . unBox) $ deRefWeak w
data Subscriber a
= forall b. SubscriberPush !(a -> EventM (Maybe b)) (PushSubscribed a b)
| forall k. GCompare k => SubscriberMerge !(k a) (MergeSubscribed k)
| forall k. (GCompare k, a ~ DMap k Identity) => SubscriberFan (FanSubscribed k)
| SubscriberHold !(Hold a)
| SubscriberSwitch (SwitchSubscribed a)
| forall b. a ~ Event b => SubscriberCoincidenceOuter (CoincidenceSubscribed b)
| SubscriberCoincidenceInner (CoincidenceSubscribed a)
showSubscriberType :: Subscriber a -> String
showSubscriberType = \case
SubscriberPush _ _ -> "SubscriberPush"
SubscriberMerge _ _ -> "SubscriberMerge"
SubscriberFan _ -> "SubscriberFan"
SubscriberHold _ -> "SubscriberHold"
SubscriberSwitch _ -> "SubscriberSwitch"
SubscriberCoincidenceOuter _ -> "SubscriberCoincidenceOuter"
SubscriberCoincidenceInner _ -> "SubscriberCoincidenceInner"
data Event a
= forall k. GCompare k => EventRoot !(k a) !(Root k)
| EventNever
| forall b. EventPush !(Push b a)
| forall k. (GCompare k, a ~ DMap k Identity) => EventMerge !(Merge k)
| forall k. GCompare k => EventFan !(k a) !(Fan k)
| EventSwitch !(Switch a)
| EventCoincidence !(Coincidence a)
showEventType :: Event a -> String
showEventType = \case
EventRoot _ _ -> "EventRoot"
EventNever -> "EventNever"
EventPush _ -> "EventPush"
EventMerge _ -> "EventMerge"
EventFan _ _ -> "EventFan"
EventSwitch _ -> "EventSwitch"
EventCoincidence _ -> "EventCoincidence"
data EventSubscribed a
= EventSubscribedRoot (RootSubscribed a)
| EventSubscribedNever
| forall b. EventSubscribedPush !(PushSubscribed b a)
| forall k. (GCompare k, a ~ DMap k Identity) => EventSubscribedMerge !(MergeSubscribed k)
| forall k. GCompare k => EventSubscribedFan !(k a) !(FanSubscribed k)
| EventSubscribedSwitch !(SwitchSubscribed a)
| EventSubscribedCoincidence !(CoincidenceSubscribed a)
newRootSubscribed :: IO (Maybe a) -> IORef [WeakSubscriber a] -> IO (RootSubscribed a)
newRootSubscribed occ subs =
return $! RootSubscribed
{ rootSubscribedOccurrence = occ
, rootSubscribedSubscribers = subs
}
newSubscriberPush :: (a -> EventM (Maybe b)) -> PushSubscribed a b -> IO (Subscriber a)
newSubscriberPush compute subd = return $! SubscriberPush compute subd
newSubscriberHold :: Hold a -> IO (Subscriber a)
newSubscriberHold h = return $! SubscriberHold h
newSubscriberFan :: GCompare k => FanSubscribed k -> IO (Subscriber (DMap k Identity))
newSubscriberFan subd = return $! SubscriberFan subd
newSubscriberSwitch :: SwitchSubscribed a -> IO (Subscriber a)
newSubscriberSwitch subd = return $! SubscriberSwitch subd
newSubscriberCoincidenceOuter :: CoincidenceSubscribed b -> IO (Subscriber (Event b))
newSubscriberCoincidenceOuter subd = return $! SubscriberCoincidenceOuter subd
newSubscriberCoincidenceInner :: CoincidenceSubscribed a -> IO (Subscriber a)
newSubscriberCoincidenceInner subd = return $! SubscriberCoincidenceInner subd
newInvalidatorSwitch :: SwitchSubscribed a -> IO Invalidator
newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
newInvalidatorPull :: Pull a -> IO Invalidator
newInvalidatorPull p = return $! InvalidatorPull p
newBox :: a -> IO (Box a)
newBox a = return $! Box a
data Behavior a
= BehaviorHold !(Hold a)
| BehaviorConst !a
| BehaviorPull !(Pull a)
type ResultM = EventM
unsafeNewIORef :: a -> b -> IORef b
unsafeNewIORef _ b = unsafePerformIO $ newIORef b
instance Functor Event where
fmap f = push $ return . Just . f
instance Functor Behavior where
fmap f = pull . liftM f . readBehaviorTracked
push :: (a -> EventM (Maybe b)) -> Event a -> Event b
push f e = EventPush $ Push
{ pushCompute = f
, pushParent = e
, pushSubscribed = unsafeNewIORef (f, e) Nothing
}
pull :: BehaviorM a -> Behavior a
pull a = BehaviorPull $ Pull
{ pullCompute = a
, pullValue = unsafeNewIORef a Nothing
}
switch :: Behavior (Event a) -> Event a
switch a = EventSwitch $ Switch
{ switchParent = a
, switchSubscribed = unsafeNewIORef a Nothing
}
coincidence :: Event (Event a) -> Event a
coincidence a = EventCoincidence $ Coincidence
{ coincidenceParent = a
, coincidenceSubscribed = unsafeNewIORef a Nothing
}
newRoot :: IO (Root k)
newRoot = do
occRef <- newIORef DMap.empty
subscribedRef <- newIORef DMap.empty
return $ Root
{ rootOccurrence = occRef
, rootSubscribed = subscribedRef
, rootInit = \_ _ -> return $ return ()
}
propagateAndUpdateSubscribersRef :: IORef [WeakSubscriber a] -> a -> EventM ()
propagateAndUpdateSubscribersRef subscribersRef a = do
subscribers <- liftIO $ readIORef subscribersRef
liftIO $ writeIORef subscribersRef []
stillAlive <- propagate a subscribers
liftIO $ modifyIORef' subscribersRef (++stillAlive)
run :: [DSum RootTrigger Identity] -> ResultM b -> IO b
run roots after = do
when debugPropagate $ putStrLn "Running an event frame"
result <- runFrame $ do
rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
occBefore <- liftIO $ do
occBefore <- readIORef occRef
writeIORef occRef $ DMap.insert k a occBefore
return occBefore
if DMap.null occBefore
then do scheduleRootClear occRef
return $ Just r
else return Nothing
forM_ (catMaybes rootsToPropagate) $ \(RootTrigger (subscribersRef, _, _) :=> Identity a) -> do
propagateAndUpdateSubscribersRef subscribersRef a
delayedRef <- EventM $ asks eventEnvDelayedMerges
let go = do
delayed <- liftIO $ readIORef delayedRef
case IntMap.minViewWithKey delayed of
Nothing -> return ()
Just ((currentHeight, current), future) -> do
when debugPropagate $ liftIO $ putStrLn $ "Running height " ++ show currentHeight
putCurrentHeight currentHeight
liftIO $ writeIORef delayedRef future
forM_ current $ \d -> case d of
DelayedMerge subscribed -> do
height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
case height `compare` currentHeight of
LT -> error "Somehow a merge's height has been decreased after it was scheduled"
GT -> scheduleMerge height subscribed
EQ -> do
m <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
liftIO $ writeIORef (mergeSubscribedAccum subscribed) DMap.empty
liftIO $ writeIORef (mergeSubscribedOccurrence subscribed) $ Just m
scheduleClear $ mergeSubscribedOccurrence subscribed
propagateAndUpdateSubscribersRef (mergeSubscribedSubscribers subscribed) m
go
go
putCurrentHeight maxBound
after
when debugPropagate $ putStrLn "Done running an event frame"
return result
data SomeMaybeIORef = forall a. SomeMaybeIORef (IORef (Maybe a))
data SomeDMapIORef = forall k. SomeDMapIORef (IORef (DMap k Identity))
data SomeAssignment = forall a. SomeAssignment (Hold a) a
data DelayedMerge = forall k. DelayedMerge (MergeSubscribed k)
debugFinalize :: Bool
debugFinalize = False
mkWeakPtrWithDebug :: a -> String -> IO (Weak a)
mkWeakPtrWithDebug x debugNote = mkWeakPtr x $
if debugFinalize
then Just $ putStrLn $ "finalizing: " ++ debugNote
else Nothing
type WeakList a = [Weak a]
traverseAndCleanWeakList_ :: Monad m => (wa -> m (Maybe a)) -> [wa] -> (a -> m ()) -> m [wa]
traverseAndCleanWeakList_ deRef ws f = go ws
where go [] = return []
go (h:t) = do
ma <- deRef h
case ma of
Just a -> do
f a
t' <- go t
return $ h : t'
Nothing -> go t
propagate :: a -> [WeakSubscriber a] -> EventM [WeakSubscriber a]
propagate a subscribers = do
traverseAndCleanWeakList_ (liftIO . deRefWeakSubscriber) subscribers $ \s -> case s of
SubscriberPush compute subscribed -> do
when debugPropagate $ liftIO $ putStrLn $ "SubscriberPush" <> showNodeId subscribed
occ <- compute a
case occ of
Nothing -> return ()
Just o -> do
liftIO $ writeIORef (pushSubscribedOccurrence subscribed) occ
scheduleClear $ pushSubscribedOccurrence subscribed
liftIO . writeIORef (pushSubscribedSubscribers subscribed) =<< propagate o =<< liftIO (readIORef (pushSubscribedSubscribers subscribed))
SubscriberMerge k subscribed -> do
when debugPropagate $ liftIO $ putStrLn $ "SubscriberMerge" <> showNodeId subscribed
oldM <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
liftIO $ writeIORef (mergeSubscribedAccum subscribed) $ DMap.insertWith (error "Same key fired multiple times for") k (Identity a) oldM
when (DMap.null oldM) $ do
height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
currentHeight <- getCurrentHeight
when (height <= currentHeight) $ error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")"
scheduleMerge height subscribed
SubscriberFan subscribed -> do
subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
when debugPropagate $ liftIO $ putStrLn $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
forM_ (DMap.toList a) $ \(k :=> Identity v) -> case DMap.lookup (FanSubscriberKey k) subs of
Nothing -> do
when debugPropagate $ liftIO $ putStrLn "No subscriber for key"
return ()
Just (Identity subsubs) -> do
_ <- propagate v subsubs
return ()
subs' <- liftIO $ forM (DMap.toList subs) $ ((\(FanSubscriberKey k :=> Identity subsubs) -> do
subsubs' <- traverseAndCleanWeakList_ (liftIO . deRefWeakSubscriber) subsubs (const $ return ())
return $ if null subsubs'
then Nothing
else Just $ FanSubscriberKey k :=> Identity subsubs') :: DSum (FanSubscriberKey k) Identity -> IO (Maybe (DSum (FanSubscriberKey k) Identity)))
liftIO $ writeIORef (fanSubscribedSubscribers subscribed) $ DMap.fromDistinctAscList $ catMaybes subs'
SubscriberHold h -> do
invalidators <- liftIO $ readIORef $ holdInvalidators h
when debugPropagate $ liftIO $ putStrLn $ "SubscriberHold" <> showNodeId h <> ": " ++ show (length invalidators)
toAssignRef <- askToAssignRef
liftIO $ modifyIORef' toAssignRef (SomeAssignment h a :)
SubscriberSwitch subscribed -> do
when debugPropagate $ liftIO $ putStrLn $ "SubscriberSwitch" <> showNodeId subscribed
liftIO $ writeIORef (switchSubscribedOccurrence subscribed) $ Just a
scheduleClear $ switchSubscribedOccurrence subscribed
subs <- liftIO $ readIORef $ switchSubscribedSubscribers subscribed
liftIO . writeIORef (switchSubscribedSubscribers subscribed) =<< propagate a subs
SubscriberCoincidenceOuter subscribed -> do
when debugPropagate $ liftIO $ putStrLn $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
outerHeight <- liftIO $ readIORef $ coincidenceSubscribedHeight subscribed
when debugPropagate $ liftIO $ putStrLn $ " outerHeight = " <> show outerHeight
(occ, innerHeight, innerSubd) <- subscribeCoincidenceInner a outerHeight subscribed
when debugPropagate $ liftIO $ putStrLn $ " isJust occ = " <> show (isJust occ)
when debugPropagate $ liftIO $ putStrLn $ " innerHeight = " <> show innerHeight
liftIO $ writeIORef (coincidenceSubscribedInnerParent subscribed) $ Just innerSubd
scheduleClear $ coincidenceSubscribedInnerParent subscribed
case occ of
Nothing -> do
when (innerHeight > outerHeight) $ liftIO $ do
writeIORef (coincidenceSubscribedHeight subscribed) innerHeight
mapM_ invalidateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
mapM_ recalculateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
Just o -> do
liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
scheduleClear $ coincidenceSubscribedOccurrence subscribed
liftIO . writeIORef (coincidenceSubscribedSubscribers subscribed) =<< propagate o =<< liftIO (readIORef (coincidenceSubscribedSubscribers subscribed))
SubscriberCoincidenceInner subscribed -> do
when debugPropagate $ liftIO $ putStrLn $ "SubscriberCoincidenceInner" <> showNodeId subscribed
liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $ Just a
scheduleClear $ coincidenceSubscribedOccurrence subscribed
liftIO . writeIORef (coincidenceSubscribedSubscribers subscribed) =<< propagate a =<< liftIO (readIORef (coincidenceSubscribedSubscribers subscribed))
data SomeCoincidenceInfo = forall a. SomeCoincidenceInfo (Weak (Subscriber a)) (Subscriber a) (Maybe (CoincidenceSubscribed a))
subscribeCoincidenceInner :: Event a -> Int -> CoincidenceSubscribed a -> EventM (Maybe a, Int, EventSubscribed a)
subscribeCoincidenceInner o outerHeight subscribedUnsafe = do
subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
wsubInner <- liftIO $ mkWeakPtrWithDebug subInner "SubscriberCoincidenceInner"
innerSubd <- (subscribe o $ WeakSubscriberSimple wsubInner)
innerOcc <- liftIO $ getEventSubscribedOcc innerSubd
innerHeight <- liftIO $ readIORef $ eventSubscribedHeightRef innerSubd
let height = max innerHeight outerHeight
emitCoincidenceInfo $ SomeCoincidenceInfo wsubInner subInner $ if height > outerHeight then Just subscribedUnsafe else Nothing
return (innerOcc, height, innerSubd)
readBehavior :: Behavior a -> IO a
readBehavior b = runBehaviorM (readBehaviorTracked b) Nothing
runBehaviorM :: BehaviorM a -> Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]) -> IO a
runBehaviorM a mwi = runReaderT (unBehaviorM a) mwi
askInvalidator :: BehaviorM (Maybe (Weak Invalidator))
askInvalidator = liftM (fmap fst) $ BehaviorM ask
askParentsRef :: BehaviorM (Maybe (IORef [SomeBehaviorSubscribed]))
askParentsRef = liftM (fmap snd) $ BehaviorM ask
readBehaviorTracked :: Behavior a -> BehaviorM a
readBehaviorTracked b = case b of
BehaviorHold h -> do
result <- liftIO $ readIORef $ holdValue h
askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
liftIO $ touch $ holdSubscriber h
return result
BehaviorConst a -> return a
BehaviorPull p -> do
val <- liftIO $ readIORef $ pullValue p
case val of
Just subscribed -> do
askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
return $ pullSubscribedValue subscribed
Nothing -> do
i <- liftIO $ newInvalidatorPull p
wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
parentsRef <- liftIO $ newIORef []
a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) $ Just (wi, parentsRef)
invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
parents <- liftIO $ readIORef parentsRef
let subscribed = PullSubscribed
{ pullSubscribedValue = a
, pullSubscribedInvalidators = invsRef
, pullSubscribedOwnInvalidator = i
, pullSubscribedParents = parents
}
liftIO $ writeIORef (pullValue p) $ Just subscribed
askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
return a
readEvent :: Event a -> ResultM (Maybe a)
readEvent e = case e of
EventRoot k r -> liftIO . liftM (coerce . DMap.lookup k) . readIORef $ rootOccurrence r
EventNever -> return Nothing
EventPush p -> do
subscribed <- getPushSubscribed p
liftIO $ do
result <- readIORef $ pushSubscribedOccurrence subscribed
touch $ pushSubscribedSelf subscribed
return result
EventMerge m -> do
subscribed <- getMergeSubscribed m
liftIO $ do
result <- readIORef $ mergeSubscribedOccurrence subscribed
touch $ mergeSubscribedSelf subscribed
return result
EventFan k f -> do
parentOcc <- readEvent $ fanParent f
return . coerce $ DMap.lookup k =<< parentOcc
EventSwitch s -> do
subscribed <- getSwitchSubscribed s
liftIO $ do
result <- readIORef $ switchSubscribedOccurrence subscribed
touch $ switchSubscribedSelf subscribed
touch $ switchSubscribedOwnInvalidator subscribed
return result
EventCoincidence c -> do
subscribed <- getCoincidenceSubscribed c
liftIO $ do
result <- readIORef $ coincidenceSubscribedOccurrence subscribed
touch $ coincidenceSubscribedOuter subscribed
return result
zeroRef :: IORef Int
zeroRef = unsafePerformIO $ newIORef 0
getEventSubscribed :: Event a -> EventM (EventSubscribed a)
getEventSubscribed e = case e of
EventRoot k r -> liftM EventSubscribedRoot $ getRootSubscribed k r
EventNever -> return EventSubscribedNever
EventPush p -> liftM EventSubscribedPush $ getPushSubscribed p
EventFan k f -> liftM (EventSubscribedFan k) $ getFanSubscribed f
EventMerge m -> liftM EventSubscribedMerge $ getMergeSubscribed m
EventSwitch s -> liftM EventSubscribedSwitch $ getSwitchSubscribed s
EventCoincidence c -> liftM EventSubscribedCoincidence $ getCoincidenceSubscribed c
debugSubscribe :: Bool
debugSubscribe = False
subscribeEventSubscribed :: EventSubscribed a -> WeakSubscriber a -> IO ()
subscribeEventSubscribed es ws = case es of
EventSubscribedRoot r -> do
when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Root"
modifyIORef' (rootSubscribedSubscribers r) (ws:)
EventSubscribedNever -> do
when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Never"
return ()
EventSubscribedPush subscribed -> do
when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Push"
modifyIORef' (pushSubscribedSubscribers subscribed) (ws:)
EventSubscribedFan k subscribed -> do
when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Fan"
modifyIORef' (fanSubscribedSubscribers subscribed) $ DMap.insertWith (liftA2 (++)) (FanSubscriberKey k) (Identity [ws])
EventSubscribedMerge subscribed -> do
when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Merge"
modifyIORef' (mergeSubscribedSubscribers subscribed) (ws:)
EventSubscribedSwitch subscribed -> do
when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Switch"
modifyIORef' (switchSubscribedSubscribers subscribed) (ws:)
EventSubscribedCoincidence subscribed -> do
when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Coincidence"
modifyIORef' (coincidenceSubscribedSubscribers subscribed) (ws:)
getEventSubscribedOcc :: EventSubscribed a -> IO (Maybe a)
getEventSubscribedOcc es = case es of
EventSubscribedRoot r -> rootSubscribedOccurrence r
EventSubscribedNever -> return Nothing
EventSubscribedPush subscribed -> readIORef $ pushSubscribedOccurrence subscribed
EventSubscribedFan k subscribed -> do
parentOcc <- getEventSubscribedOcc $ fanSubscribedParent subscribed
let occ = coerce $ DMap.lookup k =<< parentOcc
return occ
EventSubscribedMerge subscribed -> readIORef $ mergeSubscribedOccurrence subscribed
EventSubscribedSwitch subscribed -> readIORef $ switchSubscribedOccurrence subscribed
EventSubscribedCoincidence subscribed -> readIORef $ coincidenceSubscribedOccurrence subscribed
eventSubscribedHeightRef :: EventSubscribed a -> IORef Int
eventSubscribedHeightRef es = case es of
EventSubscribedRoot _ -> zeroRef
EventSubscribedNever -> zeroRef
EventSubscribedPush subscribed -> pushSubscribedHeight subscribed
EventSubscribedFan _ subscribed -> eventSubscribedHeightRef $ fanSubscribedParent subscribed
EventSubscribedMerge subscribed -> mergeSubscribedHeight subscribed
EventSubscribedSwitch subscribed -> switchSubscribedHeight subscribed
EventSubscribedCoincidence subscribed -> coincidenceSubscribedHeight subscribed
subscribe :: Event a -> WeakSubscriber a -> EventM (EventSubscribed a)
subscribe e ws = do
subd <- getEventSubscribed e
liftIO $ subscribeEventSubscribed subd ws
return subd
noinlineFalse :: Bool
noinlineFalse = False
getRootSubscribed :: GCompare k => k a -> Root k -> EventM (RootSubscribed a)
getRootSubscribed k r = do
mSubscribed <- liftIO $ readIORef $ rootSubscribed r
case DMap.lookup k mSubscribed of
Just subscribed -> return subscribed
Nothing -> liftIO $ do
subscribersRef <- newIORef []
subscribed <- newRootSubscribed (liftM (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r) subscribersRef
uninit <- rootInit r k $ RootTrigger (subscribersRef, rootOccurrence r, k)
addFinalizer subscribed $ do
when noinlineFalse $ putStr ""
uninit
liftIO $ modifyIORef' (rootSubscribed r) $ DMap.insert k subscribed
return subscribed
getPushSubscribed :: Push a b -> EventM (PushSubscribed a b)
getPushSubscribed p = do
mSubscribed <- liftIO $ readIORef $ pushSubscribed p
case mSubscribed of
Just subscribed -> return subscribed
Nothing -> do
subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ liftM fromJust $ readIORef $ pushSubscribed p
s <- liftIO $ newSubscriberPush (pushCompute p) subscribedUnsafe
ws <- liftIO $ mkWeakPtrWithDebug s "SubscriberPush"
subd <- subscribe (pushParent p) $ WeakSubscriberSimple ws
parentOcc <- liftIO $ getEventSubscribedOcc subd
occ <- liftM join $ mapM (pushCompute p) parentOcc
occRef <- liftIO $ newIORef occ
when (isJust occ) $ scheduleClear occRef
subscribersRef <- liftIO $ newIORef []
let subscribed = PushSubscribed
{ pushSubscribedOccurrence = occRef
, pushSubscribedHeight = eventSubscribedHeightRef subd
, pushSubscribedSubscribers = subscribersRef
, pushSubscribedSelf = unsafeCoerce s
, pushSubscribedParent = subd
#ifdef DEBUG_NODEIDS
, pushSubscribedNodeId = unsafeNodeId p
#endif
}
liftIO $ writeIORef (pushSubscribed p) $ Just subscribed
return subscribed
getMergeSubscribed :: forall k. GCompare k => Merge k -> EventM (MergeSubscribed k)
getMergeSubscribed m = do
mSubscribed <- liftIO $ readIORef $ mergeSubscribed m
case mSubscribed of
Just subscribed -> return subscribed
Nothing -> if DMap.null $ mergeParents m then emptyMergeSubscribed else do
subscribedRef <- liftIO $ newIORef $ error "getMergeSubscribed: subscribedRef not yet initialized"
subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
s <- liftIO $ newBox subscribedUnsafe
ws <- liftIO $ mkWeakPtrWithDebug s "SubscriberMerge"
subscribers :: [(Any, Maybe (DSum k Identity), Int, DSum k EventSubscribed)] <- forM (DMap.toList $ mergeParents m) $ \(k :=> e) -> do
parentSubd <- subscribe e $ WeakSubscriberMerge k ws
parentOcc <- liftIO $ getEventSubscribedOcc parentSubd
height <- liftIO $ readIORef $ eventSubscribedHeightRef parentSubd
return $ (unsafeCoerce s :: Any, fmap (\x -> k :=> Identity x) parentOcc, height, k :=> parentSubd)
let dm = DMap.fromDistinctAscList $ catMaybes $ map (\(_, x, _, _) -> x) subscribers
subscriberHeights = map (\(_, _, x, _) -> x) subscribers
myHeight =
if any (==invalidHeight) subscriberHeights
then invalidHeight
else succ $ Prelude.maximum subscriberHeights
currentHeight <- getCurrentHeight
let (occ, accum) = if currentHeight >= myHeight
then (if DMap.null dm then Nothing else Just dm, DMap.empty)
else (Nothing, dm)
when (not $ DMap.null accum) $ do
scheduleMerge myHeight subscribedUnsafe
occRef <- liftIO $ newIORef occ
when (isJust occ) $ scheduleClear occRef
accumRef <- liftIO $ newIORef accum
heightRef <- liftIO $ newIORef myHeight
subsRef <- liftIO $ newIORef []
let subscribed = MergeSubscribed
{ mergeSubscribedOccurrence = occRef
, mergeSubscribedAccum = accumRef
, mergeSubscribedHeight = heightRef
, mergeSubscribedSubscribers = subsRef
, mergeSubscribedSelf = unsafeCoerce $ map (\(x, _, _, _) -> x) subscribers
, mergeSubscribedParents = DMap.fromDistinctAscList $ map (\(_, _, _, x) -> x) subscribers
#ifdef DEBUG_NODEIDS
, mergeSubscribedNodeId = unsafeNodeId m
#endif
}
liftIO $ writeIORef subscribedRef subscribed
return subscribed
where emptyMergeSubscribed = do
occRef <- liftIO $ newIORef Nothing
accumRef <- liftIO $ newIORef DMap.empty
subsRef <- liftIO $ newIORef []
return $ MergeSubscribed
{ mergeSubscribedOccurrence = occRef
, mergeSubscribedAccum = accumRef
, mergeSubscribedHeight = zeroRef
, mergeSubscribedSubscribers = subsRef
, mergeSubscribedSelf = unsafeCoerce ()
, mergeSubscribedParents = DMap.empty
#ifdef DEBUG_NODEIDS
, mergeSubscribedNodeId = 1
#endif
}
getFanSubscribed :: GCompare k => Fan k -> EventM (FanSubscribed k)
getFanSubscribed f = do
mSubscribed <- liftIO $ readIORef $ fanSubscribed f
case mSubscribed of
Just subscribed -> return subscribed
Nothing -> do
subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ liftM fromJust $ readIORef $ fanSubscribed f
sub <- liftIO $ newSubscriberFan subscribedUnsafe
wsub <- liftIO $ mkWeakPtrWithDebug sub "SubscriberFan"
subd <- subscribe (fanParent f) $ WeakSubscriberSimple wsub
subscribersRef <- liftIO $ newIORef DMap.empty
let subscribed = FanSubscribed
{ fanSubscribedParent = subd
, fanSubscribedSubscribers = subscribersRef
, fanSubscribedSelf = sub
#ifdef DEBUG_NODEIDS
, fanSubscribedNodeId = unsafeNodeId f
#endif
}
liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
return subscribed
getSwitchSubscribed :: Switch a -> EventM (SwitchSubscribed a)
getSwitchSubscribed s = do
mSubscribed <- liftIO $ readIORef $ switchSubscribed s
case mSubscribed of
Just subscribed -> return subscribed
Nothing -> do
subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
i <- liftIO $ newInvalidatorSwitch subscribedUnsafe
sub <- liftIO $ newSubscriberSwitch subscribedUnsafe
wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorSwitch"
wiRef <- liftIO $ newIORef wi
wsub <- liftIO $ mkWeakPtrWithDebug sub "SubscriberSwitch"
selfWeakRef <- liftIO $ newIORef wsub
parentsRef <- liftIO $ newIORef []
e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) $ Just (wi, parentsRef)
subd <- subscribe e $ WeakSubscriberSimple wsub
subdRef <- liftIO $ newIORef subd
parentOcc <- liftIO $ getEventSubscribedOcc subd
occRef <- liftIO $ newIORef parentOcc
when (isJust parentOcc) $ scheduleClear occRef
heightRef <- liftIO $ newIORef =<< readIORef (eventSubscribedHeightRef subd)
subscribersRef <- liftIO $ newIORef []
let subscribed = SwitchSubscribed
{ switchSubscribedOccurrence = occRef
, switchSubscribedHeight = heightRef
, switchSubscribedSubscribers = subscribersRef
, switchSubscribedSelf = sub
, switchSubscribedSelfWeak = selfWeakRef
, switchSubscribedOwnInvalidator = i
, switchSubscribedOwnWeakInvalidator = wiRef
, switchSubscribedBehaviorParents = parentsRef
, switchSubscribedParent = switchParent s
, switchSubscribedCurrentParent = subdRef
#ifdef DEBUG_NODEIDS
, switchSubscribedNodeId = unsafeNodeId s
#endif
}
liftIO $ writeIORef subscribedRef subscribed
liftIO $ writeIORef (switchSubscribed s) $ Just subscribed
return subscribed
getCoincidenceSubscribed :: forall a. Coincidence a -> EventM (CoincidenceSubscribed a)
getCoincidenceSubscribed c = do
mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
case mSubscribed of
Just subscribed -> return subscribed
Nothing -> do
subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
wsubOuter <- liftIO $ mkWeakPtrWithDebug subOuter "subOuter"
outerSubd <- subscribe (coincidenceParent c) $ WeakSubscriberSimple wsubOuter
outerOcc <- liftIO $ getEventSubscribedOcc outerSubd
outerHeight <- liftIO $ readIORef $ eventSubscribedHeightRef outerSubd
(occ, height, mInnerSubd) <- case outerOcc of
Nothing -> return (Nothing, outerHeight, Nothing)
Just o -> do
(occ, height, innerSubd) <- subscribeCoincidenceInner o outerHeight subscribedUnsafe
return (occ, height, Just innerSubd)
occRef <- liftIO $ newIORef occ
when (isJust occ) $ scheduleClear occRef
heightRef <- liftIO $ newIORef height
innerSubdRef <- liftIO $ newIORef mInnerSubd
scheduleClear innerSubdRef
subscribersRef <- liftIO $ newIORef []
let subscribed = CoincidenceSubscribed
{ coincidenceSubscribedOccurrence = occRef
, coincidenceSubscribedHeight = heightRef
, coincidenceSubscribedSubscribers = subscribersRef
, coincidenceSubscribedOuter = subOuter
, coincidenceSubscribedOuterParent = outerSubd
, coincidenceSubscribedInnerParent = innerSubdRef
#ifdef DEBUG_NODEIDS
, coincidenceSubscribedNodeId = unsafeNodeId c
#endif
}
liftIO $ writeIORef subscribedRef subscribed
liftIO $ writeIORef (coincidenceSubscribed c) $ Just subscribed
return subscribed
merge :: GCompare k => DMap k Event -> Event (DMap k Identity)
merge m = EventMerge $ Merge
{ mergeParents = m
, mergeSubscribed = unsafeNewIORef m Nothing
}
newtype EventSelector k = EventSelector { select :: forall a. k a -> Event a }
fan :: GCompare k => Event (DMap k Identity) -> EventSelector k
fan e =
let f = Fan
{ fanParent = e
, fanSubscribed = unsafeNewIORef e Nothing
}
in EventSelector $ \k -> EventFan k f
runFrame :: EventM a -> IO a
runFrame a = do
toAssignRef <- newIORef []
holdInitRef <- newIORef []
heightRef <- newIORef 0
toClearRef <- newIORef []
toClearRootRef <- newIORef []
coincidenceInfosRef <- newIORef []
delayedRef <- liftIO $ newIORef IntMap.empty
result <- flip runEventM (EventEnv toAssignRef holdInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef) $ do
result <- a
let runHoldInits = do
holdInits <- liftIO $ readIORef holdInitRef
if null holdInits then return () else do
liftIO $ writeIORef holdInitRef []
forM_ holdInits $ \(SomeHoldInit e h) -> subscribeHold e h
runHoldInits
runHoldInits
return result
toClear <- readIORef toClearRef
forM_ toClear $ \(SomeMaybeIORef ref) -> writeIORef ref Nothing
toClearRoot <- readIORef toClearRootRef
forM_ toClearRoot $ \(SomeDMapIORef ref) -> writeIORef ref DMap.empty
toAssign <- readIORef toAssignRef
toReconnectRef <- newIORef []
forM_ toAssign $ \(SomeAssignment h v) -> do
writeIORef (holdValue h) v
writeIORef (holdInvalidators h) =<< invalidate toReconnectRef =<< readIORef (holdInvalidators h)
coincidenceInfos <- readIORef coincidenceInfosRef
forM_ coincidenceInfos $ \(SomeCoincidenceInfo wsubInner subInner mcs) -> do
touch subInner
finalize wsubInner
mapM_ invalidateCoincidenceHeight mcs
toReconnect <- readIORef toReconnectRef
forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
wsub <- readIORef $ switchSubscribedSelfWeak subscribed
finalize wsub
wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
finalize wi
let !i = switchSubscribedOwnInvalidator subscribed
wi' <- mkWeakPtrWithDebug i "wi'"
writeIORef (switchSubscribedBehaviorParents subscribed) []
e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed))
let !sub = switchSubscribedSelf subscribed
wsub' <- mkWeakPtrWithDebug sub "wsub'"
writeIORef (switchSubscribedSelfWeak subscribed) wsub'
subd' <- runFrame $ subscribe e $ WeakSubscriberSimple wsub'
writeIORef (switchSubscribedCurrentParent subscribed) subd'
parentHeight <- readIORef $ eventSubscribedHeightRef subd'
myHeight <- readIORef $ switchSubscribedHeight subscribed
if parentHeight == myHeight then return () else do
writeIORef (switchSubscribedHeight subscribed) parentHeight
mapM_ invalidateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
forM_ coincidenceInfos $ \(SomeCoincidenceInfo _ _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
mapM_ recalculateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
return result
invalidHeight :: Int
invalidHeight = 1000
invalidateSubscriberHeight :: WeakSubscriber a -> IO ()
invalidateSubscriberHeight ws = do
ms <- deRefWeakSubscriber ws
case ms of
Nothing -> return ()
Just s -> case s of
SubscriberPush _ subscribed -> do
when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
mapM_ invalidateSubscriberHeight =<< readIORef (pushSubscribedSubscribers subscribed)
when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
SubscriberMerge _ subscribed -> do
when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
oldHeight <- readIORef $ mergeSubscribedHeight subscribed
when (oldHeight /= invalidHeight) $ do
writeIORef (mergeSubscribedHeight subscribed) $ invalidHeight
mapM_ invalidateSubscriberHeight =<< readIORef (mergeSubscribedSubscribers subscribed)
when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
SubscriberFan subscribed -> do
when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
subscribers <- readIORef $ fanSubscribedSubscribers subscribed
forM_ (DMap.toList subscribers) $ ((\(FanSubscriberKey _ :=> Identity v) -> mapM_ invalidateSubscriberHeight v) :: DSum (FanSubscriberKey k) Identity -> IO ())
when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
SubscriberHold _ -> return ()
SubscriberSwitch subscribed -> do
when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
oldHeight <- readIORef $ switchSubscribedHeight subscribed
when (oldHeight /= invalidHeight) $ do
writeIORef (switchSubscribedHeight subscribed) $ invalidHeight
mapM_ invalidateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
SubscriberCoincidenceOuter subscribed -> do
when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
invalidateCoincidenceHeight subscribed
when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
SubscriberCoincidenceInner subscribed -> do
when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
invalidateCoincidenceHeight subscribed
when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
invalidateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
invalidateCoincidenceHeight subscribed = do
oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
when (oldHeight /= invalidHeight) $ do
writeIORef (coincidenceSubscribedHeight subscribed) $ invalidHeight
mapM_ invalidateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
recalculateSubscriberHeight :: WeakSubscriber a -> IO ()
recalculateSubscriberHeight ws = do
ms <- deRefWeakSubscriber ws
case ms of
Nothing -> return ()
Just s -> case s of
SubscriberPush _ subscribed -> do
when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
mapM_ recalculateSubscriberHeight =<< readIORef (pushSubscribedSubscribers subscribed)
when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
SubscriberMerge _ subscribed -> do
when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
oldHeight <- readIORef $ mergeSubscribedHeight subscribed
when (oldHeight == invalidHeight) $ do
height <- calculateMergeHeight subscribed
when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
when (height /= invalidHeight) $ do
writeIORef (mergeSubscribedHeight subscribed) height
mapM_ recalculateSubscriberHeight =<< readIORef (mergeSubscribedSubscribers subscribed)
when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
SubscriberFan subscribed -> do
when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
subscribers <- readIORef $ fanSubscribedSubscribers subscribed
forM_ (DMap.toList subscribers) $ ((\(FanSubscriberKey _ :=> Identity v) -> mapM_ recalculateSubscriberHeight v) :: DSum (FanSubscriberKey k) Identity -> IO ())
when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
SubscriberHold _ -> return ()
SubscriberSwitch subscribed -> do
when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
oldHeight <- readIORef $ switchSubscribedHeight subscribed
when (oldHeight == invalidHeight) $ do
height <- calculateSwitchHeight subscribed
when (height /= invalidHeight) $ do
writeIORef (switchSubscribedHeight subscribed) height
mapM_ recalculateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
SubscriberCoincidenceOuter subscribed -> do
when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
void $ recalculateCoincidenceHeight subscribed
when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
SubscriberCoincidenceInner subscribed -> do
when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
void $ recalculateCoincidenceHeight subscribed
when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
recalculateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
recalculateCoincidenceHeight subscribed = do
oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
when (oldHeight == invalidHeight) $ do
height <- calculateCoincidenceHeight subscribed
when (height /= invalidHeight) $ do
writeIORef (coincidenceSubscribedHeight subscribed) height
mapM_ recalculateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
calculateMergeHeight :: MergeSubscribed k -> IO Int
calculateMergeHeight subscribed = if DMap.null (mergeSubscribedParents subscribed) then return 0 else do
heights <- forM (DMap.toList $ mergeSubscribedParents subscribed) $ \(_ :=> es) -> do
readIORef $ eventSubscribedHeightRef es
return $ if any (== invalidHeight) heights then invalidHeight else succ $ Prelude.maximum heights
calculateSwitchHeight :: SwitchSubscribed a -> IO Int
calculateSwitchHeight subscribed = readIORef . eventSubscribedHeightRef =<< readIORef (switchSubscribedCurrentParent subscribed)
calculateCoincidenceHeight :: CoincidenceSubscribed a -> IO Int
calculateCoincidenceHeight subscribed = do
outerHeight <- readIORef $ eventSubscribedHeightRef $ coincidenceSubscribedOuterParent subscribed
innerHeight <- maybe (return 0) (readIORef . eventSubscribedHeightRef) =<< readIORef (coincidenceSubscribedInnerParent subscribed)
return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
data SomeSwitchSubscribed = forall a. SomeSwitchSubscribed (SwitchSubscribed a)
debugInvalidate :: Bool
debugInvalidate = False
invalidate :: IORef [SomeSwitchSubscribed] -> WeakList Invalidator -> IO (WeakList Invalidator)
invalidate toReconnectRef wis = do
forM_ wis $ \wi -> do
mi <- deRefWeak wi
case mi of
Nothing -> do
when debugInvalidate $ liftIO $ putStrLn "invalidate Dead"
return ()
Just i -> do
finalize wi
case i of
InvalidatorPull p -> do
when debugInvalidate $ liftIO $ putStrLn "invalidate Pull"
mVal <- readIORef $ pullValue p
forM_ mVal $ \val -> do
writeIORef (pullValue p) Nothing
writeIORef (pullSubscribedInvalidators val) =<< invalidate toReconnectRef =<< readIORef (pullSubscribedInvalidators val)
InvalidatorSwitch subscribed -> do
when debugInvalidate $ liftIO $ putStrLn "invalidate Switch"
modifyIORef' toReconnectRef (SomeSwitchSubscribed subscribed :)
return []
data Spider
instance R.Reflex Spider where
newtype Behavior Spider a = SpiderBehavior { unSpiderBehavior :: Behavior a }
newtype Event Spider a = SpiderEvent { unSpiderEvent :: Event a }
type PullM Spider = BehaviorM
type PushM Spider = EventM
never = SpiderEvent EventNever
constant = SpiderBehavior . BehaviorConst
push f = SpiderEvent. push f . unSpiderEvent
pull = SpiderBehavior . pull
merge = SpiderEvent . merge . (unsafeCoerce :: DMap k (R.Event Spider) -> DMap k Event)
fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
switch = SpiderEvent . switch . (unsafeCoerce :: Behavior (R.Event Spider a) -> Behavior (Event a)) . unSpiderBehavior
coincidence = SpiderEvent . coincidence . (unsafeCoerce :: Event (R.Event Spider a) -> Event (Event a)) . unSpiderEvent
instance R.MonadSample Spider SpiderHost where
sample = SpiderHost . readBehavior . unSpiderBehavior
instance R.MonadHold Spider SpiderHost where
hold v0 = SpiderHost . liftM SpiderBehavior . runFrame . hold v0 . unSpiderEvent
instance R.MonadSample Spider BehaviorM where
sample = readBehaviorTracked . unSpiderBehavior
instance R.MonadSample Spider EventM where
sample = liftIO . readBehavior . unSpiderBehavior
instance R.MonadHold Spider EventM where
hold v0 e = SpiderBehavior <$> hold v0 (unSpiderEvent e)
data RootTrigger a = forall k. GCompare k => RootTrigger (IORef [WeakSubscriber a], IORef (DMap k Identity), k a)
newtype SpiderEventHandle a = SpiderEventHandle { unEventHandle :: Event a }
instance R.MonadSubscribeEvent Spider SpiderHostFrame where
subscribeEvent e = SpiderHostFrame $ do
_ <- getEventSubscribed $ unSpiderEvent e
return $ SpiderEventHandle (unSpiderEvent e)
instance R.ReflexHost Spider where
type EventTrigger Spider = RootTrigger
type EventHandle Spider = SpiderEventHandle
type HostFrame Spider = SpiderHostFrame
instance R.MonadReadEvent Spider ReadPhase where
readEvent = ReadPhase . liftM (fmap return) . readEvent . unEventHandle
instance MonadRef EventM where
type Ref EventM = Ref IO
newRef = liftIO . newRef
readRef = liftIO . readRef
writeRef r a = liftIO $ writeRef r a
instance MonadAtomicRef EventM where
atomicModifyRef r f = liftIO $ atomicModifyRef r f
newtype SpiderHost a = SpiderHost { runSpiderHost :: IO a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
newtype SpiderHostFrame a = SpiderHostFrame { runSpiderHostFrame :: EventM a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
instance R.MonadSample Spider SpiderHostFrame where
sample = SpiderHostFrame . R.sample
instance R.MonadHold Spider SpiderHostFrame where
hold v0 e = SpiderHostFrame $ R.hold v0 e
newEventWithTriggerIO :: forall a. (RootTrigger a -> IO (IO ())) -> IO (Event a)
newEventWithTriggerIO f = do
es <- newFanEventWithTriggerIO $ \Refl -> f
return $ select es Refl
newFanEventWithTriggerIO :: GCompare k => (forall a. k a -> RootTrigger a -> IO (IO ())) -> IO (EventSelector k)
newFanEventWithTriggerIO f = do
occRef <- newIORef DMap.empty
subscribedRef <- newIORef DMap.empty
let !r = Root
{ rootOccurrence = occRef
, rootSubscribed = subscribedRef
, rootInit = f
}
return $ EventSelector $ \k -> EventRoot k r
instance R.MonadReflexCreateTrigger Spider SpiderHost where
newEventWithTrigger = SpiderHost . liftM SpiderEvent . newEventWithTriggerIO
newFanEventWithTrigger f = SpiderHost $ do
es <- newFanEventWithTriggerIO f
return $ R.EventSelector $ SpiderEvent . select es
instance R.MonadReflexCreateTrigger Spider SpiderHostFrame where
newEventWithTrigger = SpiderHostFrame . EventM . liftIO . liftM SpiderEvent . newEventWithTriggerIO
newFanEventWithTrigger f = SpiderHostFrame $ EventM $ liftIO $ do
es <- newFanEventWithTriggerIO f
return $ R.EventSelector $ SpiderEvent . select es
instance R.MonadSubscribeEvent Spider SpiderHost where
subscribeEvent e = SpiderHost $ do
_ <- runFrame $ getEventSubscribed $ unSpiderEvent e
return $ SpiderEventHandle (unSpiderEvent e)
newtype ReadPhase a = ReadPhase { runReadPhase :: ResultM a } deriving (Functor, Applicative, Monad, MonadFix, R.MonadSample Spider, R.MonadHold Spider)
instance R.MonadReflexHost Spider SpiderHost where
type ReadPhase SpiderHost = ReadPhase
fireEventsAndRead es (ReadPhase a) = SpiderHost $ run es a
runHostFrame = SpiderHost . runFrame . runSpiderHostFrame
instance MonadRef SpiderHost where
type Ref SpiderHost = Ref IO
newRef = SpiderHost . newRef
readRef = SpiderHost . readRef
writeRef r = SpiderHost . writeRef r
instance MonadAtomicRef SpiderHost where
atomicModifyRef r = SpiderHost . atomicModifyRef r
instance MonadRef SpiderHostFrame where
type Ref SpiderHostFrame = Ref IO
newRef = SpiderHostFrame . newRef
readRef = SpiderHostFrame . readRef
writeRef r = SpiderHostFrame . writeRef r
instance MonadAtomicRef SpiderHostFrame where
atomicModifyRef r = SpiderHostFrame . atomicModifyRef r