@@ -0,0 +1,1339 @@
1+{-# LANGUAGE CPP, ExistentialQuantification, GADTs, ScopedTypeVariables, TypeFamilies, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, BangPatterns, UndecidableInstances, EmptyDataDecls, RecursiveDo, RoleAnnotations, LambdaCase #-}
2+module Reflex.Spider.Internal where
4+import Prelude hiding (mapM, mapM_, any, sequence, concat)
6+import qualified Reflex.Class as R
7+import qualified Reflex.Host.Class as R
10+import System.Mem.Weak
12+import Data.Traversable
13+import Control.Monad hiding (mapM, mapM_, forM_, forM, sequence)
14+import Control.Monad.Reader hiding (mapM, mapM_, forM_, forM, sequence)
16+import Control.Applicative
17+import Data.Dependent.Map (DMap, DSum (..))
18+import qualified Data.Dependent.Map as DMap
19+import Data.GADT.Compare
20+import Data.Functor.Misc
22+import Data.IntMap (IntMap)
23+import qualified Data.IntMap as IntMap
24+import Control.Monad.Ref
25+import Data.Monoid ((<>))
27+import System.IO.Unsafe
29+import Control.Monad.Primitive
31+debugPropagate :: Bool
33+debugInvalidateHeight :: Bool
41+debugInvalidateHeight = True
43+class HasNodeId a where
44+ getNodeId :: a -> Int
46+instance HasNodeId (Hold a) where
47+ getNodeId = holdNodeId
49+instance HasNodeId (PushSubscribed a b) where
50+ getNodeId = pushSubscribedNodeId
52+instance HasNodeId (SwitchSubscribed a) where
53+ getNodeId = switchSubscribedNodeId
55+instance HasNodeId (MergeSubscribed a) where
56+ getNodeId = mergeSubscribedNodeId
58+instance HasNodeId (FanSubscribed a) where
59+ getNodeId = fanSubscribedNodeId
61+instance HasNodeId (CoincidenceSubscribed a) where
62+ getNodeId = coincidenceSubscribedNodeId
64+instance HasNodeId (RootSubscribed a) where
65+ getNodeId = rootSubscribedNodeId
67+showNodeId :: HasNodeId a => a -> String
68+showNodeId = ("#"<>) . show . getNodeId
72+debugPropagate = False
74+debugInvalidateHeight = False
76+showNodeId :: a -> String
82+{-# NOINLINE nextNodeIdRef #-}
83+nextNodeIdRef :: IORef Int
84+nextNodeIdRef = unsafePerformIO $ newIORef 1
86+{-# NOINLINE unsafeNodeId #-}
87+unsafeNodeId :: a -> Int
88+unsafeNodeId a = unsafePerformIO $ do
90+ atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
93+--TODO: Figure out why certain things are not 'representational', then make them representational so we can use coerce
94+--type role Hold representational
96+ = Hold { holdValue :: !(IORef a)
97+ , holdInvalidators :: !(IORef [Weak Invalidator])
98+ -- We need to use 'Any' for the next two things, because otherwise Hold inherits a nominal role for its 'a' parameter, and we want to be able to use 'coerce'
99+ , holdSubscriber :: !(IORef Any) -- Keeps its subscription alive; for some reason, a regular (or strict) reference to the Subscriber itself wasn't working, so had to use an IORef
100+ , holdParent :: !(IORef Any) -- Keeps its parent alive (will be undefined until the hold is initialized --TODO: Probably shouldn't be an IORef
107+ = EventEnv { eventEnvAssignments :: !(IORef [SomeAssignment])
108+ , eventEnvHoldInits :: !(IORef [SomeHoldInit])
109+ , eventEnvClears :: !(IORef [SomeMaybeIORef])
110+ , eventEnvCurrentHeight :: !(IORef Int)
111+ , eventEnvCoincidenceInfos :: !(IORef [SomeCoincidenceInfo])
112+ , eventEnvDelayedMerges :: !(IORef (IntMap [DelayedMerge]))
115+runEventM :: EventM a -> EventEnv -> IO a
116+runEventM = runReaderT . unEventM
118+askToAssignRef :: EventM (IORef [SomeAssignment])
119+askToAssignRef = EventM $ asks eventEnvAssignments
121+askHoldInitRef :: EventM (IORef [SomeHoldInit])
122+askHoldInitRef = EventM $ asks eventEnvHoldInits
124+getCurrentHeight :: EventM Int
125+getCurrentHeight = EventM $ do
126+ heightRef <- asks eventEnvCurrentHeight
127+ liftIO $ readIORef heightRef
129+putCurrentHeight :: Int -> EventM ()
130+putCurrentHeight h = EventM $ do
131+ heightRef <- asks eventEnvCurrentHeight
132+ liftIO $ writeIORef heightRef h
134+scheduleClear :: IORef (Maybe a) -> EventM ()
135+scheduleClear r = EventM $ do
136+ clears <- asks eventEnvClears
137+ liftIO $ modifyIORef' clears (SomeMaybeIORef r :)
139+scheduleMerge :: Int -> MergeSubscribed a -> EventM ()
140+scheduleMerge height subscribed = EventM $ do
141+ delayedRef <- asks eventEnvDelayedMerges
142+ liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) height [DelayedMerge subscribed]
144+emitCoincidenceInfo :: SomeCoincidenceInfo -> EventM ()
145+emitCoincidenceInfo sci = EventM $ do
146+ ciRef <- asks eventEnvCoincidenceInfos
147+ liftIO $ modifyIORef' ciRef (sci:)
149+-- Note: hold cannot examine its event until after the phase is over
150+hold :: a -> Event a -> EventM (Behavior a)
152+ holdInitRef <- askHoldInitRef
154+ valRef <- newIORef v0
155+ invsRef <- newIORef []
156+ parentRef <- newIORef $ error "hold not yet initialized (parent)"
157+ subscriberRef <- newIORef $ error "hold not yet initialized (subscriber)"
159+ { holdValue = valRef
160+ , holdInvalidators = invsRef
161+ , holdSubscriber = subscriberRef
162+ , holdParent = parentRef
164+ , holdNodeId = unsafeNodeId (v0, e)
167+ !s = SubscriberHold h
168+ writeIORef subscriberRef $ unsafeCoerce s
169+ modifyIORef' holdInitRef (SomeHoldInit e h :)
170+ return $ BehaviorHold h
172+subscribeHold :: Event a -> Hold a -> EventM ()
173+subscribeHold e h = do
174+ toAssignRef <- askToAssignRef
175+ !s <- liftIO $ liftM unsafeCoerce $ readIORef $ holdSubscriber h -- This must be performed strictly so that the weak pointer points at the actual item
176+ ws <- liftIO $ mkWeakPtrWithDebug s "holdSubscriber"
177+ subd <- subscribe e $ WeakSubscriberSimple ws
178+ liftIO $ writeIORef (holdParent h) $ unsafeCoerce subd
179+ occ <- liftIO $ getEventSubscribedOcc subd
181+ Nothing -> return ()
182+ Just o -> liftIO $ modifyIORef' toAssignRef (SomeAssignment h o :)
184+--type role BehaviorM representational
185+-- BehaviorM can sample behaviors
186+newtype BehaviorM a = BehaviorM { unBehaviorM :: ReaderT (Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed])) IO a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
188+data BehaviorSubscribed a
189+ = BehaviorSubscribedHold (Hold a)
190+ | BehaviorSubscribedPull (PullSubscribed a)
192+data SomeBehaviorSubscribed = forall a. SomeBehaviorSubscribed (BehaviorSubscribed a)
194+--type role PullSubscribed representational
195+data PullSubscribed a
196+ = PullSubscribed { pullSubscribedValue :: !a
197+ , pullSubscribedInvalidators :: !(IORef [Weak Invalidator])
198+ , pullSubscribedOwnInvalidator :: !Invalidator
199+ , pullSubscribedParents :: ![SomeBehaviorSubscribed] -- Need to keep parent behaviors alive, or they won't let us know when they're invalidated
202+--type role Pull representational
204+ = Pull { pullValue :: !(IORef (Maybe (PullSubscribed a)))
205+ , pullCompute :: !(BehaviorM a)
209+ = forall a. InvalidatorPull (Pull a)
210+ | forall a. InvalidatorSwitch (SwitchSubscribed a)
212+data RootSubscribed a
213+ = RootSubscribed { rootSubscribedSubscribers :: !(IORef [WeakSubscriber a])
214+ , rootSubscribedOccurrence :: !(IORef (Maybe a)) -- Alias to rootOccurrence
218+ = Root { rootOccurrence :: !(IORef (Maybe a)) -- The currently-firing occurrence of this event
219+ , rootSubscribed :: !(IORef (Maybe (RootSubscribed a)))
220+ , rootInit :: !(RootTrigger a -> IO (IO ()))
223+data SomeHoldInit = forall a. SomeHoldInit (Event a) (Hold a)
225+-- EventM can do everything BehaviorM can, plus create holds
226+newtype EventM a = EventM { unEventM :: ReaderT EventEnv IO a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO) -- The environment should be Nothing if we are not in a frame, and Just if we are - in which case it is a list of assignments to be done after the frame is over
228+data PushSubscribed a b
229+ = PushSubscribed { pushSubscribedOccurrence :: !(IORef (Maybe b)) -- If the current height is less than our height, this should always be Nothing; during our height, this will get filled in at some point, always before our children are notified; after our height, this will be filled in with the correct value (Nothing if we are not firing, Just if we are)
230+ , pushSubscribedHeight :: !(IORef Int)
231+ , pushSubscribedSubscribers :: !(IORef [WeakSubscriber b])
232+ , pushSubscribedSelf :: !(Subscriber a) -- Hold this in memory to ensure our WeakReferences don't die
233+ , pushSubscribedParent :: !(EventSubscribed a)
235+ , pushSubscribedNodeId :: Int
240+ = Push { pushCompute :: !(a -> EventM (Maybe b)) -- Compute the current firing value; assumes that its parent has been computed
241+ , pushParent :: !(Event a)
242+ , pushSubscribed :: !(IORef (Maybe (PushSubscribed a b))) --TODO: Can we replace this with an unsafePerformIO thunk?
245+data MergeSubscribed k
246+ = MergeSubscribed { mergeSubscribedOccurrence :: !(IORef (Maybe (DMap k)))
247+ , mergeSubscribedAccum :: !(IORef (DMap k)) -- This will accumulate occurrences until our height is reached, at which point it will be transferred to mergeSubscribedOccurrence
248+ , mergeSubscribedHeight :: !(IORef Int)
249+ , mergeSubscribedSubscribers :: !(IORef [WeakSubscriber (DMap k)])
250+ , mergeSubscribedSelf :: !Any -- Hold all our Subscribers in memory
251+ , mergeSubscribedParents :: !(DMap (WrapArg EventSubscribed k))
253+ , mergeSubscribedNodeId :: Int
257+--TODO: DMap sucks; we should really write something better (with a functor for the value as well as the key)
259+ = Merge { mergeParents :: !(DMap (WrapArg Event k))
260+ , mergeSubscribed :: !(IORef (Maybe (MergeSubscribed k))) --TODO: Can we replace this with an unsafePerformIO thunk?
263+data FanSubscriberKey k a where
264+ FanSubscriberKey :: k a -> FanSubscriberKey k [WeakSubscriber a]
266+instance GEq k => GEq (FanSubscriberKey k) where
267+ geq (FanSubscriberKey a) (FanSubscriberKey b) = case geq a b of
269+ Just Refl -> Just Refl
271+instance GCompare k => GCompare (FanSubscriberKey k) where
272+ gcompare (FanSubscriberKey a) (FanSubscriberKey b) = case gcompare a b of
278+ = FanSubscribed { fanSubscribedSubscribers :: !(IORef (DMap (FanSubscriberKey k)))
279+ , fanSubscribedParent :: !(EventSubscribed (DMap k))
280+ , fanSubscribedSelf :: !(Subscriber (DMap k))
282+ , fanSubscribedNodeId :: Int
287+ = Fan { fanParent :: !(Event (DMap k))
288+ , fanSubscribed :: !(IORef (Maybe (FanSubscribed k)))
291+data SwitchSubscribed a
292+ = SwitchSubscribed { switchSubscribedOccurrence :: !(IORef (Maybe a))
293+ , switchSubscribedHeight :: !(IORef Int)
294+ , switchSubscribedSubscribers :: !(IORef [WeakSubscriber a])
295+ , switchSubscribedSelf :: !(Subscriber a)
296+ , switchSubscribedSelfWeak :: !(IORef (Weak (Subscriber a)))
297+ , switchSubscribedOwnInvalidator :: !Invalidator
298+ , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak Invalidator))
299+ , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed])
300+ , switchSubscribedParent :: !(Behavior (Event a))
301+ , switchSubscribedCurrentParent :: !(IORef (EventSubscribed a))
303+ , switchSubscribedNodeId :: Int
308+ = Switch { switchParent :: !(Behavior (Event a))
309+ , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
312+data CoincidenceSubscribed a
313+ = CoincidenceSubscribed { coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
314+ , coincidenceSubscribedSubscribers :: !(IORef [WeakSubscriber a])
315+ , coincidenceSubscribedHeight :: !(IORef Int)
316+ , coincidenceSubscribedOuter :: !(Subscriber (Event a))
317+ , coincidenceSubscribedOuterParent :: !(EventSubscribed (Event a))
318+ , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed a)))
320+ , coincidenceSubscribedNodeId :: Int
325+ = Coincidence { coincidenceParent :: !(Event (Event a))
326+ , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
329+data Box a = Box { unBox :: a }
331+--type WeakSubscriber a = Weak (Subscriber a)
332+data WeakSubscriber a
333+ = forall k. GCompare k => WeakSubscriberMerge !(k a) !(Weak (Box (MergeSubscribed k))) --TODO: Can we inline the GCompare?
334+ | WeakSubscriberSimple !(Weak (Subscriber a))
336+showWeakSubscriberType :: WeakSubscriber a -> String
337+showWeakSubscriberType = \case
338+ WeakSubscriberMerge _ _ -> "WeakSubscriberMerge"
339+ WeakSubscriberSimple _ -> "WeakSubscriberSimple"
341+deRefWeakSubscriber :: WeakSubscriber a -> IO (Maybe (Subscriber a))
342+deRefWeakSubscriber ws = case ws of
343+ WeakSubscriberSimple w -> deRefWeak w
344+ WeakSubscriberMerge k w -> liftM (fmap $ SubscriberMerge k . unBox) $ deRefWeak w
347+ = forall b. SubscriberPush !(a -> EventM (Maybe b)) (PushSubscribed a b)
348+ | forall k. GCompare k => SubscriberMerge !(k a) (MergeSubscribed k) --TODO: Can we inline the GCompare?
349+ | forall k. (GCompare k, a ~ DMap k) => SubscriberFan (FanSubscribed k)
350+ | SubscriberHold !(Hold a)
351+ | SubscriberSwitch (SwitchSubscribed a)
352+ | forall b. a ~ Event b => SubscriberCoincidenceOuter (CoincidenceSubscribed b)
353+ | SubscriberCoincidenceInner (CoincidenceSubscribed a)
355+showSubscriberType :: Subscriber a -> String
356+showSubscriberType = \case
357+ SubscriberPush _ _ -> "SubscriberPush"
358+ SubscriberMerge _ _ -> "SubscriberMerge"
359+ SubscriberFan _ -> "SubscriberFan"
360+ SubscriberHold _ -> "SubscriberHold"
361+ SubscriberSwitch _ -> "SubscriberSwitch"
362+ SubscriberCoincidenceOuter _ -> "SubscriberCoincidenceOuter"
363+ SubscriberCoincidenceInner _ -> "SubscriberCoincidenceInner"
366+ = EventRoot !(Root a)
368+ | forall b. EventPush !(Push b a)
369+ | forall k. (GCompare k, a ~ DMap k) => EventMerge !(Merge k)
370+ | forall k. GCompare k => EventFan !(k a) !(Fan k)
371+ | EventSwitch !(Switch a)
372+ | EventCoincidence !(Coincidence a)
374+showEventType :: Event a -> String
375+showEventType = \case
376+ EventRoot _ -> "EventRoot"
377+ EventNever -> "EventNever"
378+ EventPush _ -> "EventPush"
379+ EventMerge _ -> "EventMerge"
380+ EventFan _ _ -> "EventFan"
381+ EventSwitch _ -> "EventSwitch"
382+ EventCoincidence _ -> "EventCoincidence"
384+data EventSubscribed a
385+ = EventSubscribedRoot !(RootSubscribed a)
386+ | EventSubscribedNever
387+ | forall b. EventSubscribedPush !(PushSubscribed b a)
388+ | forall k. (GCompare k, a ~ DMap k) => EventSubscribedMerge !(MergeSubscribed k)
389+ | forall k. GCompare k => EventSubscribedFan !(k a) !(FanSubscribed k)
390+ | EventSubscribedSwitch !(SwitchSubscribed a)
391+ | EventSubscribedCoincidence !(CoincidenceSubscribed a)
393+--type role Behavior representational
395+ = BehaviorHold !(Hold a)
397+ | BehaviorPull !(Pull a)
399+-- ResultM can read behaviors and events
400+type ResultM = EventM
402+{-# NOINLINE unsafeNewIORef #-}
403+unsafeNewIORef :: a -> b -> IORef b
404+unsafeNewIORef _ b = unsafePerformIO $ newIORef b
406+instance Functor Event where
407+ fmap f = push $ return . Just . f
409+instance Functor Behavior where
410+ fmap f = pull . liftM f . readBehaviorTracked
412+{-# NOINLINE push #-} --TODO: If this is helpful, we can get rid of the unsafeNewIORef and use unsafePerformIO directly
413+push :: (a -> EventM (Maybe b)) -> Event a -> Event b
414+push f e = EventPush $ Push
417+ , pushSubscribed = unsafeNewIORef (f, e) Nothing --TODO: Does the use of the tuple here create unnecessary overhead?
419+{-# RULES "push/push" forall f g e. push f (push g e) = push (maybe (return Nothing) f <=< g) e #-}
421+{-# NOINLINE pull #-}
422+pull :: BehaviorM a -> Behavior a
423+pull a = BehaviorPull $ Pull
425+ , pullValue = unsafeNewIORef a Nothing
427+{-# RULES "pull/pull" forall a. pull (readBehaviorTracked (pull a)) = pull a #-}
429+{-# NOINLINE switch #-}
430+switch :: Behavior (Event a) -> Event a
431+switch a = EventSwitch $ Switch
433+ , switchSubscribed = unsafeNewIORef a Nothing
435+{-# RULES "switch/constB" forall e. switch (BehaviorConst e) = e #-}
437+coincidence :: Event (Event a) -> Event a
438+coincidence a = EventCoincidence $ Coincidence
439+ { coincidenceParent = a
440+ , coincidenceSubscribed = unsafeNewIORef a Nothing
443+newRoot :: IO (Root a)
445+ occRef <- newIORef Nothing
446+ subscribedRef <- newIORef Nothing
448+ { rootOccurrence = occRef
449+ , rootSubscribed = subscribedRef
450+ , rootInit = const $ return $ return ()
453+propagateAndUpdateSubscribersRef :: IORef [WeakSubscriber a] -> a -> EventM ()
454+propagateAndUpdateSubscribersRef subscribersRef a = do
455+ subscribers <- liftIO $ readIORef subscribersRef
456+ liftIO $ writeIORef subscribersRef []
457+ stillAlive <- propagate a subscribers
458+ liftIO $ modifyIORef' subscribersRef (++stillAlive)
460+-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
461+run :: [DSum RootTrigger] -> ResultM b -> IO b
463+ when debugPropagate $ putStrLn "Running an event frame"
464+ result <- runFrame $ do
465+ forM_ roots $ \(RootTrigger (_, occRef) :=> a) -> do
466+ liftIO $ writeIORef occRef $ Just a
467+ scheduleClear occRef
468+ forM_ roots $ \(RootTrigger (subscribersRef, _) :=> a) -> do
469+ propagateAndUpdateSubscribersRef subscribersRef a
470+ delayedRef <- EventM $ asks eventEnvDelayedMerges
472+ delayed <- liftIO $ readIORef delayedRef
473+ case IntMap.minViewWithKey delayed of
474+ Nothing -> return ()
475+ Just ((currentHeight, current), future) -> do
476+ when debugPropagate $ liftIO $ putStrLn $ "Running height " ++ show currentHeight
477+ putCurrentHeight currentHeight
478+ liftIO $ writeIORef delayedRef future
479+ forM_ current $ \d -> case d of
480+ DelayedMerge subscribed -> do
481+ height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
482+ case height `compare` currentHeight of
483+ LT -> error "Somehow a merge's height has been decreased after it was scheduled"
484+ GT -> scheduleMerge height subscribed -- The height has been increased (by a coincidence event; TODO: is this the only way?)
486+ m <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
487+ liftIO $ writeIORef (mergeSubscribedAccum subscribed) DMap.empty
488+ --TODO: Assert that m is not empty
489+ liftIO $ writeIORef (mergeSubscribedOccurrence subscribed) $ Just m
490+ scheduleClear $ mergeSubscribedOccurrence subscribed
491+ propagateAndUpdateSubscribersRef (mergeSubscribedSubscribers subscribed) m
494+ putCurrentHeight maxBound
496+ when debugPropagate $ putStrLn "Done running an event frame"
499+data SomeMaybeIORef = forall a. SomeMaybeIORef (IORef (Maybe a))
501+data SomeAssignment = forall a. SomeAssignment (Hold a) a
503+data DelayedMerge = forall k. DelayedMerge (MergeSubscribed k)
505+debugFinalize :: Bool
506+debugFinalize = False
508+mkWeakPtrWithDebug :: a -> String -> IO (Weak a)
509+mkWeakPtrWithDebug x debugNote = mkWeakPtr x $
511+ then Just $ putStrLn $ "finalizing: " ++ debugNote
514+type WeakList a = [Weak a]
516+--TODO: Is it faster to clean up every time, or to occasionally go through and clean up as needed?
517+traverseAndCleanWeakList_ :: Monad m => (wa -> m (Maybe a)) -> [wa] -> (a -> m ()) -> m [wa]
518+traverseAndCleanWeakList_ deRef ws f = go ws
519+ where go [] = return []
529+-- | Propagate everything at the current height
530+propagate :: a -> [WeakSubscriber a] -> EventM [WeakSubscriber a]
531+propagate a subscribers = do
532+ traverseAndCleanWeakList_ (liftIO . deRefWeakSubscriber) subscribers $ \s -> case s of
533+ SubscriberPush compute subscribed -> do
534+ when debugPropagate $ liftIO $ putStrLn $ "SubscriberPush" <> showNodeId subscribed
537+ Nothing -> return () -- No need to write a Nothing back into the Ref
539+ liftIO $ writeIORef (pushSubscribedOccurrence subscribed) occ
540+ scheduleClear $ pushSubscribedOccurrence subscribed
541+ liftIO . writeIORef (pushSubscribedSubscribers subscribed) =<< propagate o =<< liftIO (readIORef (pushSubscribedSubscribers subscribed))
542+ SubscriberMerge k subscribed -> do
543+ when debugPropagate $ liftIO $ putStrLn $ "SubscriberMerge" <> showNodeId subscribed
544+ oldM <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
545+ liftIO $ writeIORef (mergeSubscribedAccum subscribed) $ DMap.insertWith (error "Same key fired multiple times for") k a oldM
546+ when (DMap.null oldM) $ do -- Only schedule the firing once
547+ height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
548+ --TODO: assertions about height
549+ currentHeight <- getCurrentHeight
550+ when (height <= currentHeight) $ error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")"
551+ scheduleMerge height subscribed
552+ SubscriberFan subscribed -> do
553+ subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
554+ when debugPropagate $ liftIO $ putStrLn $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
555+ --TODO: We need a better DMap intersection; here, we are assuming that the number of firing keys is small and the number of subscribers is large
556+ forM_ (DMap.toList a) $ \(k :=> v) -> case DMap.lookup (FanSubscriberKey k) subs of
558+ when debugPropagate $ liftIO $ putStrLn "No subscriber for key"
561+ _ <- propagate v subsubs --TODO: use the value of this
563+ --TODO: The following is way too slow to do all the time
564+ subs' <- liftIO $ forM (DMap.toList subs) $ ((\(FanSubscriberKey k :=> subsubs) -> do
565+ subsubs' <- traverseAndCleanWeakList_ (liftIO . deRefWeakSubscriber) subsubs (const $ return ())
566+ return $ if null subsubs' then Nothing else Just $ FanSubscriberKey k :=> subsubs') :: DSum (FanSubscriberKey k) -> IO (Maybe (DSum (FanSubscriberKey k))))
567+ liftIO $ writeIORef (fanSubscribedSubscribers subscribed) $ DMap.fromDistinctAscList $ catMaybes subs'
568+ SubscriberHold h -> do
569+ invalidators <- liftIO $ readIORef $ holdInvalidators h
570+ when debugPropagate $ liftIO $ putStrLn $ "SubscriberHold" <> showNodeId h <> ": " ++ show (length invalidators)
571+ toAssignRef <- askToAssignRef
572+ liftIO $ modifyIORef' toAssignRef (SomeAssignment h a :)
573+ SubscriberSwitch subscribed -> do
574+ when debugPropagate $ liftIO $ putStrLn $ "SubscriberSwitch" <> showNodeId subscribed
575+ liftIO $ writeIORef (switchSubscribedOccurrence subscribed) $ Just a
576+ scheduleClear $ switchSubscribedOccurrence subscribed
577+ subs <- liftIO $ readIORef $ switchSubscribedSubscribers subscribed
578+ liftIO . writeIORef (switchSubscribedSubscribers subscribed) =<< propagate a subs
579+ SubscriberCoincidenceOuter subscribed -> do
580+ when debugPropagate $ liftIO $ putStrLn $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
581+ outerHeight <- liftIO $ readIORef $ coincidenceSubscribedHeight subscribed
582+ when debugPropagate $ liftIO $ putStrLn $ " outerHeight = " <> show outerHeight
583+ (occ, innerHeight, innerSubd) <- subscribeCoincidenceInner a outerHeight subscribed
584+ when debugPropagate $ liftIO $ putStrLn $ " isJust occ = " <> show (isJust occ)
585+ when debugPropagate $ liftIO $ putStrLn $ " innerHeight = " <> show innerHeight
586+ liftIO $ writeIORef (coincidenceSubscribedInnerParent subscribed) $ Just innerSubd
587+ scheduleClear $ coincidenceSubscribedInnerParent subscribed
590+ when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
591+ writeIORef (coincidenceSubscribedHeight subscribed) innerHeight
592+ mapM_ invalidateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
593+ mapM_ recalculateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
594+ Just o -> do -- Since it's already firing, no need to adjust height
595+ liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
596+ scheduleClear $ coincidenceSubscribedOccurrence subscribed
597+ liftIO . writeIORef (coincidenceSubscribedSubscribers subscribed) =<< propagate o =<< liftIO (readIORef (coincidenceSubscribedSubscribers subscribed))
598+ SubscriberCoincidenceInner subscribed -> do
599+ when debugPropagate $ liftIO $ putStrLn $ "SubscriberCoincidenceInner" <> showNodeId subscribed
600+ liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $ Just a
601+ scheduleClear $ coincidenceSubscribedOccurrence subscribed
602+ liftIO . writeIORef (coincidenceSubscribedSubscribers subscribed) =<< propagate a =<< liftIO (readIORef (coincidenceSubscribedSubscribers subscribed))
604+data SomeCoincidenceInfo = forall a. SomeCoincidenceInfo (Weak (Subscriber a)) (Subscriber a) (Maybe (CoincidenceSubscribed a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
606+subscribeCoincidenceInner :: Event a -> Int -> CoincidenceSubscribed a -> EventM (Maybe a, Int, EventSubscribed a)
607+subscribeCoincidenceInner o outerHeight subscribedUnsafe = do
608+ let !subInner = SubscriberCoincidenceInner subscribedUnsafe
609+ wsubInner <- liftIO $ mkWeakPtrWithDebug subInner "SubscriberCoincidenceInner"
610+ innerSubd <- {-# SCC "innerSubd" #-} (subscribe o $ WeakSubscriberSimple wsubInner)
611+ innerOcc <- liftIO $ getEventSubscribedOcc innerSubd
612+ innerHeight <- liftIO $ readIORef $ eventSubscribedHeightRef innerSubd
613+ let height = max innerHeight outerHeight
614+ emitCoincidenceInfo $ SomeCoincidenceInfo wsubInner subInner $ if height > outerHeight then Just subscribedUnsafe else Nothing
615+ return (innerOcc, height, innerSubd)
617+readBehavior :: Behavior a -> IO a
618+readBehavior b = runBehaviorM (readBehaviorTracked b) Nothing --TODO: Specialize readBehaviorTracked to the Nothing and Just cases
620+runBehaviorM :: BehaviorM a -> Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]) -> IO a
621+runBehaviorM a mwi = runReaderT (unBehaviorM a) mwi
623+askInvalidator :: BehaviorM (Maybe (Weak Invalidator))
624+askInvalidator = liftM (fmap fst) $ BehaviorM ask
626+askParentsRef :: BehaviorM (Maybe (IORef [SomeBehaviorSubscribed]))
627+askParentsRef = liftM (fmap snd) $ BehaviorM ask
629+readBehaviorTracked :: Behavior a -> BehaviorM a
630+readBehaviorTracked b = case b of
631+ BehaviorHold h -> do
632+ result <- liftIO $ readIORef $ holdValue h
633+ askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
634+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
635+ liftIO $ touch $ holdSubscriber h
637+ BehaviorConst a -> return a
638+ BehaviorPull p -> do
639+ val <- liftIO $ readIORef $ pullValue p
641+ Just subscribed -> do
642+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
643+ askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
644+ liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
645+ return $ pullSubscribedValue subscribed
647+ let !i = InvalidatorPull p
648+ wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
649+ parentsRef <- liftIO $ newIORef []
650+ a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) $ Just (wi, parentsRef)
651+ invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
652+ parents <- liftIO $ readIORef parentsRef
653+ let subscribed = PullSubscribed
654+ { pullSubscribedValue = a
655+ , pullSubscribedInvalidators = invsRef
656+ , pullSubscribedOwnInvalidator = i
657+ , pullSubscribedParents = parents
659+ liftIO $ writeIORef (pullValue p) $ Just subscribed
660+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
663+readEvent :: Event a -> ResultM (Maybe a)
664+readEvent e = case e of
665+ EventRoot r -> liftIO $ readIORef $ rootOccurrence r
666+ EventNever -> return Nothing
668+ subscribed <- getPushSubscribed p
670+ result <- readIORef $ pushSubscribedOccurrence subscribed -- Since ResultM is always called after the final height is reached, this will always be valid
671+ touch $ pushSubscribedSelf subscribed
674+ subscribed <- getMergeSubscribed m
676+ result <- readIORef $ mergeSubscribedOccurrence subscribed
677+ touch $ mergeSubscribedSelf subscribed
680+ parentOcc <- readEvent $ fanParent f
681+ return $ DMap.lookup k =<< parentOcc
683+ subscribed <- getSwitchSubscribed s
685+ result <- readIORef $ switchSubscribedOccurrence subscribed
686+ touch $ switchSubscribedSelf subscribed
687+ touch $ switchSubscribedOwnInvalidator subscribed
689+ EventCoincidence c -> do
690+ subscribed <- getCoincidenceSubscribed c
692+ result <- readIORef $ coincidenceSubscribedOccurrence subscribed
693+ touch $ coincidenceSubscribedOuter subscribed
694+ --TODO: do we need to touch the inner subscriber?
697+-- Always refers to 0
698+{-# NOINLINE zeroRef #-}
700+zeroRef = unsafePerformIO $ newIORef 0
702+getEventSubscribed :: Event a -> EventM (EventSubscribed a)
703+getEventSubscribed e = case e of
704+ EventRoot r -> liftM EventSubscribedRoot $ getRootSubscribed r
705+ EventNever -> return EventSubscribedNever
706+ EventPush p -> liftM EventSubscribedPush $ getPushSubscribed p
707+ EventFan k f -> liftM (EventSubscribedFan k) $ getFanSubscribed f
708+ EventMerge m -> liftM EventSubscribedMerge $ getMergeSubscribed m
709+ EventSwitch s -> liftM EventSubscribedSwitch $ getSwitchSubscribed s
710+ EventCoincidence c -> liftM EventSubscribedCoincidence $ getCoincidenceSubscribed c
712+debugSubscribe :: Bool
713+debugSubscribe = False
715+subscribeEventSubscribed :: EventSubscribed a -> WeakSubscriber a -> IO ()
716+subscribeEventSubscribed es ws = case es of
717+ EventSubscribedRoot r -> do
718+ when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Root"
719+ modifyIORef' (rootSubscribedSubscribers r) (ws:)
720+ EventSubscribedNever -> do
721+ when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Never"
723+ EventSubscribedPush subscribed -> do
724+ when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Push"
725+ modifyIORef' (pushSubscribedSubscribers subscribed) (ws:)
726+ EventSubscribedFan k subscribed -> do
727+ when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Fan"
728+ modifyIORef' (fanSubscribedSubscribers subscribed) $ DMap.insertWith (++) (FanSubscriberKey k) [ws]
729+ EventSubscribedMerge subscribed -> do
730+ when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Merge"
731+ modifyIORef' (mergeSubscribedSubscribers subscribed) (ws:)
732+ EventSubscribedSwitch subscribed -> do
733+ when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Switch"
734+ modifyIORef' (switchSubscribedSubscribers subscribed) (ws:)
735+ EventSubscribedCoincidence subscribed -> do
736+ when debugSubscribe $ liftIO $ putStrLn $ "subscribeEventSubscribed Coincidence"
737+ modifyIORef' (coincidenceSubscribedSubscribers subscribed) (ws:)
739+getEventSubscribedOcc :: EventSubscribed a -> IO (Maybe a)
740+getEventSubscribedOcc es = case es of
741+ EventSubscribedRoot r -> readIORef $ rootSubscribedOccurrence r
742+ EventSubscribedNever -> return Nothing
743+ EventSubscribedPush subscribed -> readIORef $ pushSubscribedOccurrence subscribed
744+ EventSubscribedFan k subscribed -> do
745+ parentOcc <- getEventSubscribedOcc $ fanSubscribedParent subscribed
746+ let occ = DMap.lookup k =<< parentOcc
748+ EventSubscribedMerge subscribed -> readIORef $ mergeSubscribedOccurrence subscribed
749+ EventSubscribedSwitch subscribed -> readIORef $ switchSubscribedOccurrence subscribed
750+ EventSubscribedCoincidence subscribed -> readIORef $ coincidenceSubscribedOccurrence subscribed
752+eventSubscribedHeightRef :: EventSubscribed a -> IORef Int
753+eventSubscribedHeightRef es = case es of
754+ EventSubscribedRoot _ -> zeroRef
755+ EventSubscribedNever -> zeroRef
756+ EventSubscribedPush subscribed -> pushSubscribedHeight subscribed
757+ EventSubscribedFan _ subscribed -> eventSubscribedHeightRef $ fanSubscribedParent subscribed
758+ EventSubscribedMerge subscribed -> mergeSubscribedHeight subscribed
759+ EventSubscribedSwitch subscribed -> switchSubscribedHeight subscribed
760+ EventSubscribedCoincidence subscribed -> coincidenceSubscribedHeight subscribed
762+subscribe :: Event a -> WeakSubscriber a -> EventM (EventSubscribed a)
764+ subd <- getEventSubscribed e
765+ liftIO $ subscribeEventSubscribed subd ws
768+getRootSubscribed :: Root a -> EventM (RootSubscribed a)
769+getRootSubscribed r = do
770+ mSubscribed <- liftIO $ readIORef $ rootSubscribed r
772+ Just subscribed -> return subscribed
773+ Nothing -> liftIO $ do
774+ subscribersRef <- newIORef []
775+ let !subscribed = RootSubscribed
776+ { rootSubscribedOccurrence = rootOccurrence r
777+ , rootSubscribedSubscribers = subscribersRef
779+ -- Strangely, init needs the same stuff as a RootSubscribed has, but it must not be the same as the one that everyone's subscribing to, or it'll leak memory
780+ uninit <- rootInit r $ RootTrigger (subscribersRef, rootOccurrence r)
781+ addFinalizer subscribed $ do
782+-- putStrLn "Uninit root"
784+ liftIO $ writeIORef (rootSubscribed r) $ Just subscribed
787+-- When getPushSubscribed returns, the PushSubscribed returned will have a fully filled-in
788+getPushSubscribed :: Push a b -> EventM (PushSubscribed a b)
789+getPushSubscribed p = do
790+ mSubscribed <- liftIO $ readIORef $ pushSubscribed p
792+ Just subscribed -> return subscribed
793+ Nothing -> do -- Not yet subscribed
794+ subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ liftM fromJust $ readIORef $ pushSubscribed p
795+ let !s = SubscriberPush (pushCompute p) subscribedUnsafe
796+ ws <- liftIO $ mkWeakPtrWithDebug s "SubscriberPush"
797+ subd <- subscribe (pushParent p) $ WeakSubscriberSimple ws
798+ parentOcc <- liftIO $ getEventSubscribedOcc subd
799+ occ <- liftM join $ mapM (pushCompute p) parentOcc
800+ occRef <- liftIO $ newIORef occ
801+ when (isJust occ) $ scheduleClear occRef
802+ subscribersRef <- liftIO $ newIORef []
803+ let subscribed = PushSubscribed
804+ { pushSubscribedOccurrence = occRef
805+ , pushSubscribedHeight = eventSubscribedHeightRef subd -- Since pushes have the same height as their parents, share the ref
806+ , pushSubscribedSubscribers = subscribersRef
807+ , pushSubscribedSelf = unsafeCoerce s
808+ , pushSubscribedParent = subd
810+ , pushSubscribedNodeId = unsafeNodeId p
813+ liftIO $ writeIORef (pushSubscribed p) $ Just subscribed
816+getMergeSubscribed :: forall k. GCompare k => Merge k -> EventM (MergeSubscribed k)
817+getMergeSubscribed m = {-# SCC "getMergeSubscribed.entire" #-} do
818+ mSubscribed <- liftIO $ readIORef $ mergeSubscribed m
820+ Just subscribed -> return subscribed
821+ Nothing -> if DMap.null $ mergeParents m then emptyMergeSubscribed else do
822+ subscribedRef <- liftIO $ newIORef $ error "getMergeSubscribed: subscribedRef not yet initialized"
823+ subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
824+ let !s = Box subscribedUnsafe
825+ ws <- liftIO $ mkWeakPtrWithDebug s "SubscriberMerge"
826+ subscribers :: [(Any, Maybe (DSum k), Int, DSum (WrapArg EventSubscribed k))] <- forM (DMap.toList $ mergeParents m) $ {-# SCC "getMergeSubscribed.a" #-} \(WrapArg k :=> e) -> {-# SCC "getMergeSubscribed.a1" #-} do
827+ parentSubd <- {-# SCC "getMergeSubscribed.a.parentSubd" #-} subscribe e $ WeakSubscriberMerge k ws
828+ parentOcc <- {-# SCC "getMergeSubscribed.a.parentOcc" #-} liftIO $ getEventSubscribedOcc parentSubd
829+ height <- {-# SCC "getMergeSubscribed.a.height" #-} liftIO $ readIORef $ eventSubscribedHeightRef parentSubd
830+ return $ {-# SCC "getMergeSubscribed.a.returnVal" #-} (unsafeCoerce s :: Any, fmap (k :=>) parentOcc, height, WrapArg k :=> parentSubd)
831+ let dm = DMap.fromDistinctAscList $ catMaybes $ map (\(_, x, _, _) -> x) subscribers
832+ subscriberHeights = map (\(_, _, x, _) -> x) subscribers
834+ if any (==invalidHeight) subscriberHeights --TODO: Replace 'any' with invalidHeight-preserving 'maximum'
836+ else succ $ Prelude.maximum subscriberHeights -- This is safe because the DMap will never be empty here
837+ currentHeight <- getCurrentHeight
838+ let (occ, accum) = if currentHeight >= myHeight -- If we should have fired by now
839+ then (if DMap.null dm then Nothing else Just dm, DMap.empty)
841+ when (not $ DMap.null accum) $ do
842+ scheduleMerge myHeight subscribedUnsafe
843+ occRef <- liftIO $ newIORef occ
844+ when (isJust occ) $ scheduleClear occRef
845+ accumRef <- liftIO $ newIORef accum
846+ heightRef <- liftIO $ newIORef myHeight
847+ subsRef <- liftIO $ newIORef []
848+ let subscribed = MergeSubscribed
849+ { mergeSubscribedOccurrence = occRef
850+ , mergeSubscribedAccum = accumRef
851+ , mergeSubscribedHeight = heightRef
852+ , mergeSubscribedSubscribers = subsRef
853+ , mergeSubscribedSelf = unsafeCoerce $ map (\(x, _, _, _) -> x) subscribers --TODO: Does lack of strictness make this leak?
854+ , mergeSubscribedParents = DMap.fromDistinctAscList $ map (\(_, _, _, x) -> x) subscribers
856+ , mergeSubscribedNodeId = unsafeNodeId m
859+ liftIO $ writeIORef subscribedRef subscribed
861+ where emptyMergeSubscribed = do --TODO: This should never happen
862+ occRef <- liftIO $ newIORef Nothing
863+ accumRef <- liftIO $ newIORef DMap.empty
864+ subsRef <- liftIO $ newIORef []
865+ return $ MergeSubscribed
866+ { mergeSubscribedOccurrence = occRef
867+ , mergeSubscribedAccum = accumRef
868+ , mergeSubscribedHeight = zeroRef
869+ , mergeSubscribedSubscribers = subsRef --TODO: This will definitely leak
870+ , mergeSubscribedSelf = unsafeCoerce ()
871+ , mergeSubscribedParents = DMap.empty
873+ , mergeSubscribedNodeId = -1
877+getFanSubscribed :: GCompare k => Fan k -> EventM (FanSubscribed k)
878+getFanSubscribed f = do
879+ mSubscribed <- liftIO $ readIORef $ fanSubscribed f
881+ Just subscribed -> return subscribed
883+ subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ liftM fromJust $ readIORef $ fanSubscribed f
884+ let !sub = SubscriberFan subscribedUnsafe
885+ wsub <- liftIO $ mkWeakPtrWithDebug sub "SubscriberFan"
886+ subd <- subscribe (fanParent f) $ WeakSubscriberSimple wsub
887+ subscribersRef <- liftIO $ newIORef DMap.empty
888+ let subscribed = FanSubscribed
889+ { fanSubscribedParent = subd
890+ , fanSubscribedSubscribers = subscribersRef
891+ , fanSubscribedSelf = sub
893+ , fanSubscribedNodeId = unsafeNodeId f
896+ liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
899+getSwitchSubscribed :: Switch a -> EventM (SwitchSubscribed a)
900+getSwitchSubscribed s = do
901+ mSubscribed <- liftIO $ readIORef $ switchSubscribed s
903+ Just subscribed -> return subscribed
905+ subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
906+ subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
907+ let !i = InvalidatorSwitch subscribedUnsafe
908+ !sub = SubscriberSwitch subscribedUnsafe
909+ wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorSwitch"
910+ wiRef <- liftIO $ newIORef wi
911+ wsub <- liftIO $ mkWeakPtrWithDebug sub "SubscriberSwitch"
912+ selfWeakRef <- liftIO $ newIORef wsub
913+ parentsRef <- liftIO $ newIORef [] --TODO: This should be unnecessary, because it will always be filled with just the single parent behavior
914+ e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) $ Just (wi, parentsRef)
915+ subd <- subscribe e $ WeakSubscriberSimple wsub
916+ subdRef <- liftIO $ newIORef subd
917+ parentOcc <- liftIO $ getEventSubscribedOcc subd
918+ occRef <- liftIO $ newIORef parentOcc
919+ when (isJust parentOcc) $ scheduleClear occRef
920+ heightRef <- liftIO $ newIORef =<< readIORef (eventSubscribedHeightRef subd)
921+ subscribersRef <- liftIO $ newIORef []
922+ let subscribed = SwitchSubscribed
923+ { switchSubscribedOccurrence = occRef
924+ , switchSubscribedHeight = heightRef
925+ , switchSubscribedSubscribers = subscribersRef
926+ , switchSubscribedSelf = sub
927+ , switchSubscribedSelfWeak = selfWeakRef
928+ , switchSubscribedOwnInvalidator = i
929+ , switchSubscribedOwnWeakInvalidator = wiRef
930+ , switchSubscribedBehaviorParents = parentsRef
931+ , switchSubscribedParent = switchParent s
932+ , switchSubscribedCurrentParent = subdRef
934+ , switchSubscribedNodeId = unsafeNodeId s
937+ liftIO $ writeIORef subscribedRef subscribed
938+ liftIO $ writeIORef (switchSubscribed s) $ Just subscribed
941+getCoincidenceSubscribed :: forall a. Coincidence a -> EventM (CoincidenceSubscribed a)
942+getCoincidenceSubscribed c = do
943+ mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
945+ Just subscribed -> return subscribed
947+ subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
948+ subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
949+ let !subOuter = SubscriberCoincidenceOuter subscribedUnsafe
950+ wsubOuter <- liftIO $ mkWeakPtrWithDebug subOuter "subOuter"
951+ outerSubd <- subscribe (coincidenceParent c) $ WeakSubscriberSimple wsubOuter
952+ outerOcc <- liftIO $ getEventSubscribedOcc outerSubd
953+ outerHeight <- liftIO $ readIORef $ eventSubscribedHeightRef outerSubd
954+ (occ, height, mInnerSubd) <- case outerOcc of
955+ Nothing -> return (Nothing, outerHeight, Nothing)
957+ (occ, height, innerSubd) <- subscribeCoincidenceInner o outerHeight subscribedUnsafe
958+ return (occ, height, Just innerSubd)
959+ occRef <- liftIO $ newIORef occ
960+ when (isJust occ) $ scheduleClear occRef
961+ heightRef <- liftIO $ newIORef height
962+ innerSubdRef <- liftIO $ newIORef mInnerSubd
963+ scheduleClear innerSubdRef
964+ subscribersRef <- liftIO $ newIORef []
965+ let subscribed = CoincidenceSubscribed
966+ { coincidenceSubscribedOccurrence = occRef
967+ , coincidenceSubscribedHeight = heightRef
968+ , coincidenceSubscribedSubscribers = subscribersRef
969+ , coincidenceSubscribedOuter = subOuter
970+ , coincidenceSubscribedOuterParent = outerSubd
971+ , coincidenceSubscribedInnerParent = innerSubdRef
973+ , coincidenceSubscribedNodeId = unsafeNodeId c
976+ liftIO $ writeIORef subscribedRef subscribed
977+ liftIO $ writeIORef (coincidenceSubscribed c) $ Just subscribed
980+merge :: GCompare k => DMap (WrapArg Event k) -> Event (DMap k)
981+merge m = EventMerge $ Merge
983+ , mergeSubscribed = unsafeNewIORef m Nothing
986+newtype EventSelector k = EventSelector { select :: forall a. k a -> Event a }
988+fan :: GCompare k => Event (DMap k) -> EventSelector k
992+ , fanSubscribed = unsafeNewIORef e Nothing
994+ in EventSelector $ \k -> EventFan k f
996+-- | Run an event action outside of a frame
997+runFrame :: EventM a -> IO a
999+ toAssignRef <- newIORef [] -- This should only actually get used when events are firing
1000+ holdInitRef <- newIORef []
1001+ heightRef <- newIORef 0
1002+ toClearRef <- newIORef []
1003+ coincidenceInfosRef <- newIORef []
1004+ delayedRef <- liftIO $ newIORef IntMap.empty
1005+ result <- flip runEventM (EventEnv toAssignRef holdInitRef toClearRef heightRef coincidenceInfosRef delayedRef) $ do
1007+ let runHoldInits = do
1008+ holdInits <- liftIO $ readIORef holdInitRef
1009+ if null holdInits then return () else do
1010+ liftIO $ writeIORef holdInitRef []
1011+ forM_ holdInits $ \(SomeHoldInit e h) -> subscribeHold e h
1013+ runHoldInits -- This must happen before doing the assignments, in case subscribing a Hold causes existing Holds to be read by the newly-propagated events
1015+ toClear <- readIORef toClearRef
1016+ forM_ toClear $ \(SomeMaybeIORef ref) -> writeIORef ref Nothing
1017+ toAssign <- readIORef toAssignRef
1018+ toReconnectRef <- newIORef []
1019+ forM_ toAssign $ \(SomeAssignment h v) -> do
1020+ writeIORef (holdValue h) v
1021+ writeIORef (holdInvalidators h) =<< invalidate toReconnectRef =<< readIORef (holdInvalidators h)
1022+ coincidenceInfos <- readIORef coincidenceInfosRef
1023+ forM_ coincidenceInfos $ \(SomeCoincidenceInfo wsubInner subInner mcs) -> do
1026+ mapM_ invalidateCoincidenceHeight mcs
1027+ toReconnect <- readIORef toReconnectRef
1028+ forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1029+ wsub <- readIORef $ switchSubscribedSelfWeak subscribed
1031+ wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
1033+ let !i = switchSubscribedOwnInvalidator subscribed
1034+ wi' <- mkWeakPtrWithDebug i "wi'"
1035+ writeIORef (switchSubscribedBehaviorParents subscribed) []
1036+ e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed))
1037+ --TODO: Make sure we touch the pieces of the SwitchSubscribed at the appropriate times
1038+ let !sub = switchSubscribedSelf subscribed -- Must be done strictly, or the weak pointer will refer to a useless thunk
1039+ wsub' <- mkWeakPtrWithDebug sub "wsub'"
1040+ writeIORef (switchSubscribedSelfWeak subscribed) wsub'
1041+ subd' <- runFrame $ subscribe e $ WeakSubscriberSimple wsub' --TODO: Assert that the event isn't firing --TODO: This should not loop because none of the events should be firing, but still, it is inefficient
1043+ stackTrace <- liftIO $ liftM renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
1044+ liftIO $ putStrLn $ (++stackTrace) $ "subd' subscribed to " ++ case e of
1045+ EventRoot _ -> "EventRoot"
1046+ EventNever -> "EventNever"
1047+ _ -> "something else"
1049+ writeIORef (switchSubscribedCurrentParent subscribed) subd'
1050+ parentHeight <- readIORef $ eventSubscribedHeightRef subd'
1051+ myHeight <- readIORef $ switchSubscribedHeight subscribed
1052+ if parentHeight == myHeight then return () else do
1053+ writeIORef (switchSubscribedHeight subscribed) parentHeight
1054+ mapM_ invalidateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1055+ forM_ coincidenceInfos $ \(SomeCoincidenceInfo _ _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
1056+ forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1057+ mapM_ recalculateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1060+invalidHeight :: Int
1061+invalidHeight = -1000
1063+invalidateSubscriberHeight :: WeakSubscriber a -> IO ()
1064+invalidateSubscriberHeight ws = do
1065+ ms <- deRefWeakSubscriber ws
1067+ Nothing -> return () --TODO: cleanup?
1068+ Just s -> case s of
1069+ SubscriberPush _ subscribed -> do
1070+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1071+ mapM_ invalidateSubscriberHeight =<< readIORef (pushSubscribedSubscribers subscribed)
1072+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1073+ SubscriberMerge _ subscribed -> do
1074+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1075+ oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1076+ when (oldHeight /= invalidHeight) $ do
1077+ writeIORef (mergeSubscribedHeight subscribed) $ invalidHeight
1078+ mapM_ invalidateSubscriberHeight =<< readIORef (mergeSubscribedSubscribers subscribed)
1079+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1080+ SubscriberFan subscribed -> do
1081+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1082+ subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1083+ forM_ (DMap.toList subscribers) $ ((\(FanSubscriberKey _ :=> v) -> mapM_ invalidateSubscriberHeight v) :: DSum (FanSubscriberKey k) -> IO ())
1084+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1085+ SubscriberHold _ -> return ()
1086+ SubscriberSwitch subscribed -> do
1087+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1088+ oldHeight <- readIORef $ switchSubscribedHeight subscribed
1089+ when (oldHeight /= invalidHeight) $ do
1090+ writeIORef (switchSubscribedHeight subscribed) $ invalidHeight
1091+ mapM_ invalidateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1092+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1093+ SubscriberCoincidenceOuter subscribed -> do
1094+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1095+ invalidateCoincidenceHeight subscribed
1096+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1097+ SubscriberCoincidenceInner subscribed -> do
1098+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1099+ invalidateCoincidenceHeight subscribed
1100+ when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1102+invalidateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
1103+invalidateCoincidenceHeight subscribed = do
1104+ oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1105+ when (oldHeight /= invalidHeight) $ do
1106+ writeIORef (coincidenceSubscribedHeight subscribed) $ invalidHeight
1107+ mapM_ invalidateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed)
1109+--TODO: The recalculation algorithm seems a bit funky; make sure it doesn't miss stuff or hit stuff twice; also, it should probably be lazy
1111+recalculateSubscriberHeight :: WeakSubscriber a -> IO ()
1112+recalculateSubscriberHeight ws = do
1113+ ms <- deRefWeakSubscriber ws
1115+ Nothing -> return () --TODO: cleanup?
1116+ Just s -> case s of
1117+ SubscriberPush _ subscribed -> do
1118+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1119+ mapM_ recalculateSubscriberHeight =<< readIORef (pushSubscribedSubscribers subscribed)
1120+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1121+ SubscriberMerge _ subscribed -> do
1122+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1123+ oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1124+ when (oldHeight == invalidHeight) $ do
1125+ height <- calculateMergeHeight subscribed
1126+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1127+ when (height /= invalidHeight) $ do -- If height == invalidHeight, that means some of the prereqs have not yet been recomputed; when they do recompute, they'll catch this node again --TODO: this is O(n*m), where n is the number of children of this noe and m is the number that have been invalidated
1128+ writeIORef (mergeSubscribedHeight subscribed) height
1129+ mapM_ recalculateSubscriberHeight =<< readIORef (mergeSubscribedSubscribers subscribed)
1130+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1131+ SubscriberFan subscribed -> do
1132+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1133+ subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1134+ forM_ (DMap.toList subscribers) $ ((\(FanSubscriberKey _ :=> v) -> mapM_ recalculateSubscriberHeight v) :: DSum (FanSubscriberKey k) -> IO ())
1135+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1136+ SubscriberHold _ -> return ()
1137+ SubscriberSwitch subscribed -> do
1138+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1139+ oldHeight <- readIORef $ switchSubscribedHeight subscribed
1140+ when (oldHeight == invalidHeight) $ do
1141+ height <- calculateSwitchHeight subscribed
1142+ when (height /= invalidHeight) $ do
1143+ writeIORef (switchSubscribedHeight subscribed) height
1144+ mapM_ recalculateSubscriberHeight =<< readIORef (switchSubscribedSubscribers subscribed)
1145+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1146+ SubscriberCoincidenceOuter subscribed -> do
1147+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1148+ void $ recalculateCoincidenceHeight subscribed
1149+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1150+ SubscriberCoincidenceInner subscribed -> do
1151+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1152+ void $ recalculateCoincidenceHeight subscribed
1153+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1155+recalculateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
1156+recalculateCoincidenceHeight subscribed = do
1157+ oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1158+ when (oldHeight == invalidHeight) $ do
1159+ height <- calculateCoincidenceHeight subscribed
1160+ when (height /= invalidHeight) $ do
1161+ writeIORef (coincidenceSubscribedHeight subscribed) height
1162+ mapM_ recalculateSubscriberHeight =<< readIORef (coincidenceSubscribedSubscribers subscribed) --TODO: This should probably be mandatory, just like with the merge and switch ones
1164+calculateMergeHeight :: MergeSubscribed k -> IO Int
1165+calculateMergeHeight subscribed = if DMap.null (mergeSubscribedParents subscribed) then return 0 else do
1166+ heights <- forM (DMap.toList $ mergeSubscribedParents subscribed) $ \(WrapArg _ :=> es) -> do
1167+ readIORef $ eventSubscribedHeightRef es
1168+ return $ if any (== invalidHeight) heights then invalidHeight else succ $ Prelude.maximum heights --TODO: Replace 'any' with invalidHeight-preserving 'maximum'
1170+calculateSwitchHeight :: SwitchSubscribed a -> IO Int
1171+calculateSwitchHeight subscribed = readIORef . eventSubscribedHeightRef =<< readIORef (switchSubscribedCurrentParent subscribed)
1173+calculateCoincidenceHeight :: CoincidenceSubscribed a -> IO Int
1174+calculateCoincidenceHeight subscribed = do
1175+ outerHeight <- readIORef $ eventSubscribedHeightRef $ coincidenceSubscribedOuterParent subscribed
1176+ innerHeight <- maybe (return 0) (readIORef . eventSubscribedHeightRef) =<< readIORef (coincidenceSubscribedInnerParent subscribed)
1177+ return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
1180+recalculateEventSubscribedHeight :: EventSubscribed a -> IO Int
1181+recalculateEventSubscribedHeight es = case es of
1182+ EventSubscribedRoot _ -> return 0
1183+ EventSubscribedNever -> return 0
1184+ EventSubscribedPush subscribed -> recalculateEventSubscribedHeight $ pushSubscribedParent subscribed
1185+ EventSubscribedMerge subscribed -> do
1186+ oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1187+ if oldHeight /= invalidHeight then return oldHeight else do
1188+ height <- calculateMergeHeight subscribed
1189+ writeIORef (mergeSubscribedHeight subscribed) height
1191+ EventSubscribedFan _ subscribed -> recalculateEventSubscribedHeight $ fanSubscribedParent subscribed
1192+ EventSubscribedSwitch subscribed -> do
1193+ oldHeight <- readIORef $ switchSubscribedHeight subscribed
1194+ if oldHeight /= invalidHeight then return oldHeight else do
1195+ height <- calculateSwitchHeight subscribed
1196+ writeIORef (switchSubscribedHeight subscribed) height
1198+ EventSubscribedCoincidence subscribed -> recalculateCoincidenceHeight subscribed
1201+data SomeSwitchSubscribed = forall a. SomeSwitchSubscribed (SwitchSubscribed a)
1203+debugInvalidate :: Bool
1204+debugInvalidate = False
1206+invalidate :: IORef [SomeSwitchSubscribed] -> WeakList Invalidator -> IO (WeakList Invalidator)
1207+invalidate toReconnectRef wis = do
1208+ forM_ wis $ \wi -> do
1212+ when debugInvalidate $ liftIO $ putStrLn "invalidate Dead"
1213+ return () --TODO: Should we clean this up here?
1215+ finalize wi -- Once something's invalidated, it doesn't need to hang around; this will change when some things are strict
1217+ InvalidatorPull p -> do
1218+ when debugInvalidate $ liftIO $ putStrLn "invalidate Pull"
1219+ mVal <- readIORef $ pullValue p
1220+ forM_ mVal $ \val -> do
1221+ writeIORef (pullValue p) Nothing
1222+ writeIORef (pullSubscribedInvalidators val) =<< invalidate toReconnectRef =<< readIORef (pullSubscribedInvalidators val)
1223+ InvalidatorSwitch subscribed -> do
1224+ when debugInvalidate $ liftIO $ putStrLn "invalidate Switch"
1225+ modifyIORef' toReconnectRef (SomeSwitchSubscribed subscribed :)
1226+ return [] -- Since we always finalize everything, always return an empty list --TODO: There are some things that will need to be re-subscribed every time; we should try to avoid finalizing them
1228+--------------------------------------------------------------------------------
1229+-- Reflex integration
1230+--------------------------------------------------------------------------------
1234+instance R.Reflex Spider where
1235+ newtype Behavior Spider a = SpiderBehavior { unSpiderBehavior :: Behavior a }
1236+ newtype Event Spider a = SpiderEvent { unSpiderEvent :: Event a }
1237+ type PullM Spider = BehaviorM
1238+ type PushM Spider = EventM
1239+ {-# INLINE never #-}
1240+ {-# INLINE constant #-}
1241+ never = SpiderEvent EventNever
1242+ constant = SpiderBehavior . BehaviorConst
1243+ push f = SpiderEvent. push f . unSpiderEvent
1244+ pull = SpiderBehavior . pull
1245+ merge = SpiderEvent . merge . (unsafeCoerce :: DMap (WrapArg (R.Event Spider) k) -> DMap (WrapArg Event k))
1246+ fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
1247+ switch = SpiderEvent . switch . (unsafeCoerce :: Behavior (R.Event Spider a) -> Behavior (Event a)) . unSpiderBehavior
1248+ coincidence = SpiderEvent . coincidence . (unsafeCoerce :: Event (R.Event Spider a) -> Event (Event a)) . unSpiderEvent
1250+instance R.MonadSample Spider SpiderHost where
1251+ {-# INLINE sample #-}
1252+ sample = SpiderHost . readBehavior . unSpiderBehavior
1254+instance R.MonadHold Spider SpiderHost where
1255+ hold v0 = SpiderHost . liftM SpiderBehavior . runFrame . hold v0 . unSpiderEvent
1257+instance R.MonadSample Spider BehaviorM where
1258+ {-# INLINE sample #-}
1259+ sample = readBehaviorTracked . unSpiderBehavior
1261+instance R.MonadSample Spider EventM where
1262+ {-# INLINE sample #-}
1263+ sample = liftIO . readBehavior . unSpiderBehavior
1265+instance R.MonadHold Spider EventM where
1266+ {-# INLINE hold #-}
1267+ hold v0 e = SpiderBehavior <$> hold v0 (unSpiderEvent e)
1269+newtype RootTrigger a = RootTrigger (IORef [WeakSubscriber a], IORef (Maybe a))
1271+instance R.ReflexHost Spider where
1272+ type EventTrigger Spider = RootTrigger
1273+ type EventHandle Spider = R.Event Spider
1274+ type HostFrame Spider = SpiderHostFrame
1276+instance R.MonadReadEvent Spider ResultM where
1277+ {-# INLINE readEvent #-}
1278+ readEvent = liftM (fmap return) . readEvent . unSpiderEvent
1280+instance MonadRef EventM where
1281+ type Ref EventM = Ref IO
1282+ {-# INLINE newRef #-}
1283+ {-# INLINE readRef #-}
1284+ {-# INLINE writeRef #-}
1285+ {-# INLINE atomicModifyRef #-}
1286+ newRef = liftIO . newRef
1287+ readRef = liftIO . readRef
1288+ writeRef r a = liftIO $ writeRef r a
1289+ atomicModifyRef r f = liftIO $ atomicModifyRef r f
1291+newtype SpiderHost a = SpiderHost { runSpiderHost :: IO a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO)
1293+newtype SpiderHostFrame a = SpiderHostFrame { runSpiderHostFrame :: EventM a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO)
1295+instance R.MonadSample Spider SpiderHostFrame where
1296+ sample = SpiderHostFrame . R.sample --TODO: This can cause problems with laziness, so we should get rid of it if we can
1298+instance R.MonadHold Spider SpiderHostFrame where
1299+ {-# INLINE hold #-}
1300+ hold v0 e = SpiderHostFrame $ R.hold v0 e
1302+newEventWithTriggerIO :: (RootTrigger a -> IO (IO ())) -> IO (R.Event Spider a)
1303+newEventWithTriggerIO f = do
1304+ occRef <- newIORef Nothing
1305+ subscribedRef <- newIORef Nothing
1307+ { rootOccurrence = occRef
1308+ , rootSubscribed = subscribedRef
1311+ return $ SpiderEvent $ EventRoot r
1313+instance R.MonadReflexCreateTrigger Spider SpiderHost where
1314+ newEventWithTrigger = SpiderHost . newEventWithTriggerIO
1316+instance R.MonadReflexCreateTrigger Spider SpiderHostFrame where
1317+ newEventWithTrigger = SpiderHostFrame . EventM . liftIO . newEventWithTriggerIO
1319+instance R.MonadReflexHost Spider SpiderHost where
1320+ fireEventsAndRead es a = SpiderHost $ run es a
1321+ subscribeEvent e = SpiderHost $ do
1322+ _ <- runFrame $ getEventSubscribed $ unSpiderEvent e --TODO: The result of this should actually be used
1324+ runFrame = SpiderHost . runFrame
1325+ runHostFrame = SpiderHost . runFrame . runSpiderHostFrame
1327+instance MonadRef SpiderHost where
1328+ type Ref SpiderHost = Ref IO
1329+ newRef = SpiderHost . newRef
1330+ readRef = SpiderHost . readRef
1331+ writeRef r = SpiderHost . writeRef r
1332+ atomicModifyRef r = SpiderHost . atomicModifyRef r
1334+instance MonadRef SpiderHostFrame where
1335+ type Ref SpiderHostFrame = Ref IO
1336+ newRef = SpiderHostFrame . newRef
1337+ readRef = SpiderHostFrame . readRef
1338+ writeRef r = SpiderHostFrame . writeRef r
1339+ atomicModifyRef r = SpiderHostFrame . atomicModifyRef r