@@ -8,9 +8,7 @@
88{-# LANGUAGE FunctionalDependencies #-}
99{-# LANGUAGE GADTs #-}
1010{-# LANGUAGE GeneralizedNewtypeDeriving #-}
11−#if defined(DEBUG) || defined(DEBUG_CYCLES)
1211{-# LANGUAGE LambdaCase #-}
13−#endif
1412{-# LANGUAGE MagicHash #-}
1513{-# LANGUAGE MultiParamTypeClasses #-}
1614{-# LANGUAGE MultiWayIf #-}
@@ -59,9 +57,12 @@ import Data.Proxy
5957import Data.These
6058import Data.Traversable
6159import Data.Word
60+import Data.FastMutableIntMap (FastMutableIntMap, PatchIntMap (..))
61+import qualified Data.FastMutableIntMap as FastMutableIntMap
6262import GHC.Exts
6363import GHC.IORef (IORef (..))
6464import GHC.Stack
65+import Reflex.FastWeak
6566import Reflex.FunctorMaybe
6667import System.IO.Unsafe
6768import System.Mem.Weak
@@ -75,6 +76,8 @@ import Data.Monoid (mempty)
7576import Data.Tree (Forest, Tree (..), drawForest)
7677#endif
7778
79+import Data.FastWeakBag (FastWeakBag)
80+import qualified Data.FastWeakBag as FastWeakBag
7881import Data.WeakBag (WeakBag, WeakBagTicket, _weakBag_children)
7982import qualified Data.WeakBag as WeakBag
8083#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
@@ -240,22 +243,57 @@ subscribeAndRead = unEvent
240243-- | Construct an 'Event' equivalent to that constructed by 'push', but with no
241244-- caching; if the computation function is very cheap, this is (much) more
242245-- efficient than 'push'
243−{-# NOINLINE pushCheap #-}
246+{-# INLINE [1] pushCheap #-}
244247pushCheap :: HasSpiderTimeline x => (a -> ComputeM x (Maybe b)) -> Event x a -> Event x b
245−pushCheap f e = Event $ \sub -> do
248+pushCheap !f e = Event $ \sub -> do
246249 (subscription, occ) <- subscribeAndRead e $ sub
247250 { subscriberPropagate = \a -> do
248− mb <- runComputeM $ f a
251+ mb <- f a
249252 mapM_ (subscriberPropagate sub) mb
250253 }
251− occ' <- join <$> mapM (runComputeM . f) occ
254+ occ' <- join <$> mapM f occ
252255 return (subscription, occ')
253256
257+-- | A subscriber that never triggers other 'Event's
258+{-# INLINE terminalSubscriber #-}
259+terminalSubscriber :: (a -> EventM x ()) -> Subscriber x a
260+terminalSubscriber p = Subscriber
261+ { subscriberPropagate = p
262+ , subscriberInvalidateHeight = \_ -> return ()
263+ , subscriberRecalculateHeight = \_ -> return ()
264+ }
265+
266+-- | Subscribe to an Event only for the duration of one occurrence
267+{-# INLINE subscribeAndReadHead #-}
268+subscribeAndReadHead :: Event x a -> Subscriber x a -> EventM x (EventSubscription x, Maybe a)
269+subscribeAndReadHead e sub = do
270+ subscriptionRef <- liftIO $ newIORef $ error "subscribeAndReadHead: not initialized"
271+ (subscription, occ) <- subscribeAndRead e $ sub
272+ { subscriberPropagate = \a -> do
273+ liftIO $ unsubscribe =<< readIORef subscriptionRef
274+ subscriberPropagate sub a
275+ }
276+ liftIO $ case occ of
277+ Nothing -> writeIORef subscriptionRef $! subscription
278+ Just _ -> unsubscribe subscription
279+ return (subscription, occ)
280+
281+--TODO: Make this lazy in its input event
282+headE :: (MonadIO m, Defer (SomeMergeInit x) m) => Event x a -> m (Event x a)
283+headE originalE = do
284+ parent <- liftIO $ newIORef $ Just originalE
285+ defer $ SomeMergeInit $ do --TODO: Rename SomeMergeInit appropriately
286+ let clearParent = liftIO $ writeIORef parent Nothing
287+ (_, occ) <- subscribeAndReadHead originalE $ terminalSubscriber $ \_ -> clearParent
288+ when (isJust occ) clearParent
289+ return $ Event $ \sub -> do
290+ liftIO (readIORef parent) >>= \case
291+ Nothing -> return (EventSubscription (return ()) $ EventSubscribed zeroRef $ toAny (), Nothing)
292+ Just e -> subscribeAndReadHead e sub
293+
254294data CacheSubscribed x a
255− = CacheSubscribed { _cacheSubscribed_subscribers :: {-# UNPACK #-} !(WeakBag (Subscriber x a))
256− , _cacheSubscribed_weakSelf :: {-# UNPACK #-} !(IORef (Weak (CacheSubscribed x a)))
295+ = CacheSubscribed { _cacheSubscribed_subscribers :: {-# UNPACK #-} !(FastWeakBag (Subscriber x a))
257296 , _cacheSubscribed_parent :: {-# UNPACK #-} !(EventSubscription x)
258− , _cacheSubscribed_cached :: {-# UNPACK #-} !(IORef (Maybe (CacheSubscribed x a)))
259297 , _cacheSubscribed_occurrence :: {-# UNPACK #-} !(IORef (Maybe a))
260298 }
261299
@@ -263,62 +301,67 @@ data CacheSubscribed x a
263301-- repeatedly
264302--
265303--TODO: Try a caching strategy where we subscribe directly to the parent when
266−--there's only one subscriber, and then build our own WeakBag only when a second
304+--there's only one subscriber, and then build our own FastWeakBag only when a second
267305--subscriber joins
268−{-# NOINLINE cacheEvent #-}
269−cacheEvent :: HasSpiderTimeline x => Event x a -> Event x a
306+{-# NOINLINE [0] cacheEvent #-}
307+cacheEvent :: forall x a. HasSpiderTimeline x => Event x a -> Event x a
270308cacheEvent e =
271309#ifdef DEBUG_TRACE_EVENTS
272310 withStackOneLine $ \callSite -> Event $
273311#else
274312 Event $
275313#endif
276− let !mSubscribedRef = unsafeNewIORef e Nothing
314+ let mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
315+ !mSubscribedRef = unsafeNewIORef e emptyFastWeak
277316 in \sub -> {-# SCC "cacheEvent" #-} do
278317#ifdef DEBUG_TRACE_EVENTS
279318 unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
280319#endif
281− mSubscribed <- liftIO $ readIORef mSubscribedRef
282− case mSubscribed of
283− Just subscribed -> liftIO $ do
284− ticket <- WeakBag.insert sub (_cacheSubscribed_subscribers subscribed) (_cacheSubscribed_weakSelf subscribed) cleanupCacheSubscribed
285− occ <- readIORef $ _cacheSubscribed_occurrence subscribed
286− let es = EventSubscribed
287− { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ _cacheSubscribed_parent subscribed
288− , eventSubscribedRetained = toAny subscribed
289− }
290− return (EventSubscription (WeakBag.remove ticket >> touch ticket) es, occ)
320+ subscribedTicket <- liftIO (readIORef mSubscribedRef >>= getFastWeakTicket) >>= \case
321+ Just subscribedTicket -> return subscribedTicket
291322 Nothing -> do
292− weakSelf <- liftIO $ newIORef $ error "cacheEvent: weakSelf not yet intialized"
293− (subscribers, ticket) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCacheSubscribed
294− occRef <- liftIO $ newIORef undefined
323+ subscribers <- liftIO FastWeakBag.empty
324+ occRef <- liftIO $ newIORef Nothing -- This should never be read prior to being set below
295325 (parentSub, occ) <- subscribeAndRead e $ Subscriber
296326 { subscriberPropagate = \a -> do
297327 liftIO $ writeIORef occRef $ Just a
298328 scheduleClear occRef
299− propagate a subscribers
329+ propagateFast a subscribers
300330 , subscriberInvalidateHeight = \old -> do
301− WeakBag.traverse subscribers $ invalidateSubscriberHeight old
331+ FastWeakBag.traverse subscribers $ invalidateSubscriberHeight old
302332 , subscriberRecalculateHeight = \new -> do
303− WeakBag.traverse subscribers $ recalculateSubscriberHeight new
333+ FastWeakBag.traverse subscribers $ recalculateSubscriberHeight new
304334 }
305− liftIO $ writeIORef occRef occ
306− when (isJust occ) $ scheduleClear occRef
335+ when (isJust occ) $ do
336+ liftIO $ writeIORef occRef occ -- Set the initial value of occRef; we don't need to do this if occ is Nothing
337+ scheduleClear occRef
307338 let !subscribed = CacheSubscribed
308339 { _cacheSubscribed_subscribers = subscribers
309− , _cacheSubscribed_weakSelf = weakSelf
310340 , _cacheSubscribed_parent = parentSub
311− , _cacheSubscribed_cached = mSubscribedRef
312341 , _cacheSubscribed_occurrence = occRef
313342 }
314− liftIO $ writeIORef mSubscribedRef $ Just subscribed
315− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "PushSubscribed"
316− return (EventSubscription (WeakBag.remove ticket >> touch ticket) (EventSubscribed (eventSubscribedHeightRef $ _eventSubscription_subscribed parentSub) (toAny subscribed)), occ)
317−
318−cleanupCacheSubscribed :: CacheSubscribed x a -> IO ()
319−cleanupCacheSubscribed self = do
320− unsubscribe $ _cacheSubscribed_parent self
321− writeIORef (_cacheSubscribed_cached self) Nothing
343+ subscribedTicket <- liftIO $ mkFastWeakTicket subscribed
344+ liftIO $ writeIORef mSubscribedRef =<< getFastWeakTicketWeak subscribedTicket
345+ return subscribedTicket
346+ liftIO $ do
347+ subscribed <- getFastWeakTicketValue subscribedTicket
348+ ticket <- FastWeakBag.insert sub $ _cacheSubscribed_subscribers subscribed
349+ occ <- readIORef $ _cacheSubscribed_occurrence subscribed
350+ let es = EventSubscription
351+ { _eventSubscription_unsubscribe = do
352+ FastWeakBag.remove ticket
353+ isEmpty <- FastWeakBag.isEmpty $ _cacheSubscribed_subscribers subscribed
354+ when isEmpty $ do
355+ writeIORef mSubscribedRef emptyFastWeak
356+ unsubscribe $ _cacheSubscribed_parent subscribed
357+ touch ticket
358+ touch subscribedTicket
359+ , _eventSubscription_subscribed = EventSubscribed
360+ { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ _cacheSubscribed_parent subscribed
361+ , eventSubscribedRetained = toAny subscribedTicket
362+ }
363+ }
364+ return (es, occ)
322365
323366subscribe :: Event x a -> Subscriber x a -> EventM x (EventSubscription x)
324367subscribe e s = fst <$> subscribeAndRead e s
@@ -352,7 +395,7 @@ eventDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Event x p
352395eventDyn !j = Event $ \sub -> getDynHold j >>= \h -> subscribeHoldEvent h sub
353396
354397{-# INLINE subscribeCoincidenceInner #-}
355−subscribeCoincidenceInner :: CanSubscribe x m => Event x a -> Height -> CoincidenceSubscribed x a -> m (Maybe a, Height, EventSubscribed x)
398+subscribeCoincidenceInner :: HasSpiderTimeline x => Event x a -> Height -> CoincidenceSubscribed x a -> EventM x (Maybe a, Height, EventSubscribed x)
356399subscribeCoincidenceInner inner outerHeight subscribedUnsafe = do
357400 subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
358401 (subscription@(EventSubscription _ innerSubd), innerOcc) <- subscribeAndRead inner subInner
@@ -497,6 +540,13 @@ propagate a subscribers = withIncreasedDepth $ do
497540 --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
498541 WeakBag.traverse subscribers $ \s -> subscriberPropagate s a
499542
543+-- | Propagate everything at the current height
544+propagateFast :: a -> FastWeakBag (Subscriber x a) -> EventM x ()
545+propagateFast a subscribers = withIncreasedDepth $ do
546+ -- Note: in the following traversal, we do not visit nodes that are added to the list during our traversal; they are new events, which will necessarily have full information already, so there is no need to traverse them
547+ --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
548+ FastWeakBag.traverse subscribers $ \s -> subscriberPropagate s a
549+
500550--------------------------------------------------------------------------------
501551-- EventSubscribed
502552--------------------------------------------------------------------------------
@@ -595,7 +645,7 @@ walkInvalidHeightParents s0 = do
595645#endif
596646
597647{-# INLINE subscribeHoldEvent #-}
598−subscribeHoldEvent :: CanSubscribe x m => Hold x p -> Subscriber x p -> m (EventSubscription x, Maybe p)
648+subscribeHoldEvent :: HasSpiderTimeline x => Hold x p -> Subscriber x p -> EventM x (EventSubscription x, Maybe p)
599649subscribeHoldEvent = subscribeAndRead . holdEvent
600650
601651--------------------------------------------------------------------------------
@@ -751,6 +801,7 @@ data EventEnv x
751801 , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
752802 , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
753803 , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
804+ , eventEnvIntClears :: !(IORef [SomeIntClear])
754805 , eventEnvRootClears :: !(IORef [SomeRootClear])
755806 , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
756807 , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
@@ -835,6 +886,14 @@ instance HasSpiderTimeline x => Defer SomeClear (EventM x) where
835886scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
836887scheduleClear r = defer $ SomeClear r
837888
889+instance HasSpiderTimeline x => Defer SomeIntClear (EventM x) where
890+ {-# INLINE getDeferralQueue #-}
891+ getDeferralQueue = asksEventEnv eventEnvIntClears
892+
893+{-# INLINE scheduleIntClear #-}
894+scheduleIntClear :: Defer SomeIntClear m => IORef (IntMap a) -> m ()
895+scheduleIntClear r = defer $ SomeIntClear r
896+
838897instance HasSpiderTimeline x => Defer SomeRootClear (EventM x) where
839898 {-# INLINE getDeferralQueue #-}
840899 getDeferralQueue = asksEventEnv eventEnvRootClears
@@ -870,7 +929,7 @@ hold v0 e = do
870929 return h
871930
872931{-# INLINE getHoldEventSubscription #-}
873−getHoldEventSubscription :: forall m p x. (Defer (SomeAssignment x) m, Defer (SomeHoldInit x) m, Defer SomeClear m, Patch p, HasCurrentHeight x m, Defer (SomeMergeInit x) m, Defer (SomeResetCoincidence x) m, HasSpiderTimeline x, m ~ EventM x) => Hold x p -> m (EventSubscription x)
932+getHoldEventSubscription :: forall p x. (HasSpiderTimeline x, Patch p) => Hold x p -> EventM x (EventSubscription x)
874933getHoldEventSubscription h = do
875934 ep <- liftIO $ readIORef $ holdParent h
876935 case ep of
@@ -970,21 +1029,6 @@ newtype SomeMergeInit x = SomeMergeInit { unSomeMergeInit :: EventM x () }
9701029-- EventM can do everything BehaviorM can, plus create holds
9711030newtype EventM x a = EventM { unEventM :: IO a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadException, MonadAsyncException) -- 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
9721031
973−newtype ComputeM x a = ComputeM (ReaderT (IORef [SomeHoldInit x], IORef [SomeDynInit x]) IO a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
974−
975−instance Defer (SomeHoldInit x) (ComputeM x) where
976− getDeferralQueue = ComputeM $ asks fst
977−
978−instance Defer (SomeDynInit x) (ComputeM x) where
979− getDeferralQueue = ComputeM $ asks snd
980−
981−{-# INLINABLE runComputeM #-}
982−runComputeM :: (Defer (SomeHoldInit x) m, Defer (SomeDynInit x) m) => ComputeM x a -> m a
983−runComputeM (ComputeM a) = do
984− holds <- getDeferralQueue
985− dyns <- getDeferralQueue
986− liftIO $ runReaderT a (holds, dyns)
987−
9881032newtype MergeSubscribedParent x a = MergeSubscribedParent { unMergeSubscribedParent :: EventSubscription x }
9891033
9901034data MergeSubscribedParentWithMove x k a = MergeSubscribedParentWithMove
@@ -1111,7 +1155,7 @@ instance HasSpiderTimeline x => FunctorMaybe (Event x) where
11111155
11121156instance HasSpiderTimeline x => Align (Event x) where
11131157 nil = eventNever
1114− align ea eb = fmapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
1158+ align ea eb = fmapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
11151159
11161160newtype Dyn x p = Dyn { unDyn :: IORef (Either (BehaviorM x (PatchTarget p), Event x p) (Hold x p)) }
11171161
@@ -1230,9 +1274,11 @@ scheduleMerge' initialHeight heightRef a = scheduleMerge initialHeight $ do
12301274 GT -> scheduleMerge' height heightRef a -- The height has been increased (by a coincidence event; TODO: is this the only way?)
12311275 EQ -> a
12321276
1233−data SomeClear = forall a. SomeClear (IORef (Maybe a))
1277+data SomeClear = forall a. SomeClear {-# UNPACK #-} !(IORef (Maybe a))
1278+
1279+data SomeIntClear = forall a. SomeIntClear {-# UNPACK #-} !(IORef (IntMap a))
12341280
1235−data SomeRootClear = forall k. SomeRootClear (IORef (DMap k Identity))
1281+data SomeRootClear = forall k. SomeRootClear {-# UNPACK #-} !(IORef (DMap k Identity))
12361282
12371283data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
12381284
@@ -1363,16 +1409,6 @@ getDynHold d = do
13631409zeroRef :: IORef Height
13641410zeroRef = unsafePerformIO $ newIORef zeroHeight
13651411
1366−type CanSubscribe x m =
1367− ( HasSpiderTimeline x
1368− , HasCurrentHeight x m
1369− , Defer (SomeHoldInit x) m, Defer (SomeMergeInit x) m -- The things we need to lazily initialize state
1370− , Defer SomeClear m -- Wiping out event firings
1371− , Defer (SomeAssignment x) m -- Updating state
1372− , Defer (SomeResetCoincidence x) m
1373− , m ~ EventM x
1374− )
1375−
13761412getRootSubscribed :: GCompare k => k a -> Root x k -> Subscriber x a -> IO (WeakBagTicket, RootSubscribed x a, Maybe a)
13771413getRootSubscribed k r sub = do
13781414 mSubscribed <- readIORef $ rootSubscribed r
@@ -1428,8 +1464,71 @@ cleanupRootSubscribed self@RootSubscribed { rootSubscribedKey = k, rootSubscribe
14281464subscribeRootSubscribed :: RootSubscribed x a -> Subscriber x a -> IO WeakBagTicket
14291465subscribeRootSubscribed subscribed sub = WeakBag.insert sub (rootSubscribedSubscribers subscribed) (rootSubscribedWeakSelf subscribed) cleanupRootSubscribed
14301466
1467+newtype EventSelectorInt x a = EventSelectorInt { selectInt :: Int -> Event x a }
1468+
1469+data FanInt x a = FanInt
1470+ { _fanInt_subscribers :: {-# UNPACK #-} !(FastMutableIntMap (FastWeakBag (Subscriber x a))) --TODO: Clean up the keys in here when their child weak bags get empty --TODO: Remove our own subscription when the subscribers list is completely empty
1471+ , _fanInt_subscriptionRef :: {-# UNPACK #-} !(IORef (EventSubscription x)) -- This should have a valid subscription iff subscribers is non-empty
1472+ , _fanInt_occRef :: {-# UNPACK #-} !(IORef (IntMap a))
1473+ }
1474+
1475+newFanInt :: IO (FanInt x a)
1476+newFanInt = do
1477+ subscribers <- FastMutableIntMap.newEmpty --TODO: Clean up the keys in here when their child weak bags get empty --TODO: Remove our own subscription when the subscribers list is completely empty
1478+ subscriptionRef <- newIORef $ error "fanInt: no subscription"
1479+ occRef <- newIORef $ error "fanInt: no occurrence"
1480+ return $ FanInt
1481+ { _fanInt_subscribers = subscribers
1482+ , _fanInt_subscriptionRef = subscriptionRef
1483+ , _fanInt_occRef = occRef
1484+ }
1485+
1486+{-# NOINLINE unsafeNewFanInt #-}
1487+unsafeNewFanInt :: b -> FanInt x a
1488+unsafeNewFanInt b = unsafePerformIO $ do
1489+ touch b
1490+ newFanInt
1491+
1492+fanInt :: HasSpiderTimeline x => Event x (IntMap a) -> EventSelectorInt x a
1493+fanInt p =
1494+ let self = unsafeNewFanInt p
1495+ in EventSelectorInt $ \k -> Event $ \sub -> do
1496+ isEmpty <- liftIO $ FastMutableIntMap.isEmpty (_fanInt_subscribers self)
1497+ when isEmpty $ do -- This is the first subscriber, so we need to subscribe to our input
1498+ (subscription, parentOcc) <- subscribeAndRead p $ Subscriber
1499+ { subscriberPropagate = \m -> do
1500+ liftIO $ writeIORef (_fanInt_occRef self) m
1501+ scheduleIntClear $ _fanInt_occRef self
1502+ FastMutableIntMap.forIntersectionWithImmutable_ (_fanInt_subscribers self) m $ \b v -> do --TODO: Do we need to know that no subscribers are being added as we traverse?
1503+ FastWeakBag.traverse b $ \s -> do
1504+ subscriberPropagate s v
1505+ , subscriberInvalidateHeight = \old -> do
1506+ FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1507+ FastWeakBag.traverse b $ \s -> do
1508+ subscriberInvalidateHeight s old
1509+ , subscriberRecalculateHeight = \new -> do
1510+ FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1511+ FastWeakBag.traverse b $ \s -> do
1512+ subscriberRecalculateHeight s new
1513+ }
1514+ liftIO $ do
1515+ writeIORef (_fanInt_subscriptionRef self) subscription
1516+ writeIORef (_fanInt_occRef self) $ fromMaybe IntMap.empty parentOcc
1517+ scheduleIntClear $ _fanInt_occRef self
1518+ liftIO $ do
1519+ b <- FastMutableIntMap.lookup (_fanInt_subscribers self) k >>= \case
1520+ Nothing -> do
1521+ b <- FastWeakBag.empty
1522+ FastMutableIntMap.insert (_fanInt_subscribers self) k b
1523+ return b
1524+ Just b -> return b
1525+ t <- liftIO $ FastWeakBag.insert sub b
1526+ currentOcc <- readIORef (_fanInt_occRef self)
1527+ (EventSubscription _ (EventSubscribed !heightRef _)) <- readIORef (_fanInt_subscriptionRef self)
1528+ return (EventSubscription (FastWeakBag.remove t) $! EventSubscribed heightRef $! toAny (_fanInt_subscriptionRef self, t), IntMap.lookup k currentOcc)
1529+
14311530{-# INLINABLE getFanSubscribed #-}
1432−getFanSubscribed :: (Defer (SomeAssignment x) m, Defer (SomeHoldInit x) m, Defer (SomeMergeInit x) m, Defer SomeClear m, HasCurrentHeight x m, Defer (SomeResetCoincidence x) m, HasSpiderTimeline x, m ~ EventM x, GCompare k) => k a -> Fan x k -> Subscriber x a -> m (WeakBagTicket, FanSubscribed x k, Maybe a)
1531+getFanSubscribed :: (HasSpiderTimeline x, GCompare k) => k a -> Fan x k -> Subscriber x a -> EventM x (WeakBagTicket, FanSubscribed x k, Maybe a)
14331532getFanSubscribed k f sub = do
14341533 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
14351534 case mSubscribed of
@@ -1488,7 +1587,7 @@ subscribeFanSubscribed k subscribed sub = do
14881587 Just (FanSubscribedChildren list _ weakSelf) -> {-# SCC "hitSubscribeFanSubscribed" #-} WeakBag.insert sub list weakSelf cleanupFanSubscribed
14891588
14901589{-# INLINABLE getSwitchSubscribed #-}
1491−getSwitchSubscribed :: (Defer (SomeHoldInit x) m, Defer (SomeAssignment x) m, Defer (SomeMergeInit x) m, Defer SomeClear m, Defer (SomeResetCoincidence x) m, HasCurrentHeight x m, HasSpiderTimeline x, m ~ EventM x) => Switch x a -> Subscriber x a -> m (WeakBagTicket, SwitchSubscribed x a, Maybe a)
1590+getSwitchSubscribed :: HasSpiderTimeline x => Switch x a -> Subscriber x a -> EventM x (WeakBagTicket, SwitchSubscribed x a, Maybe a)
14921591getSwitchSubscribed s sub = do
14931592 mSubscribed <- liftIO $ readIORef $ switchSubscribed s
14941593 case mSubscribed of
@@ -1544,7 +1643,7 @@ subscribeSwitchSubscribed :: SwitchSubscribed x a -> Subscriber x a -> IO WeakBa
15441643subscribeSwitchSubscribed subscribed sub = WeakBag.insert sub (switchSubscribedSubscribers subscribed) (switchSubscribedWeakSelf subscribed) cleanupSwitchSubscribed
15451644
15461645{-# INLINABLE getCoincidenceSubscribed #-}
1547−getCoincidenceSubscribed :: forall x a m. (Defer (SomeAssignment x) m, Defer (SomeHoldInit x) m, Defer (SomeMergeInit x) m, Defer SomeClear m, HasCurrentHeight x m, Defer (SomeResetCoincidence x) m, HasSpiderTimeline x, m ~ EventM x) => Coincidence x a -> Subscriber x a -> m (WeakBagTicket, CoincidenceSubscribed x a, Maybe a)
1646+getCoincidenceSubscribed :: forall x a. HasSpiderTimeline x => Coincidence x a -> Subscriber x a -> EventM x (WeakBagTicket, CoincidenceSubscribed x a, Maybe a)
15481647getCoincidenceSubscribed c sub = do
15491648 mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
15501649 case mSubscribed of
@@ -1607,7 +1706,7 @@ mergeWithMove d = cacheEvent (mergeCheapWithMove d)
16071706
16081707{-# INLINE [1] mergeCheap #-}
16091708mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1610−mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy DMap.size
1709+mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
16111710 where
16121711 updateMe :: MergeUpdateFunc k x (PatchDMap k (Event x)) (MergeSubscribedParent x)
16131712 updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
@@ -1642,7 +1741,7 @@ mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy DMap.size
16421741
16431742{-# INLINE [1] mergeCheapWithMove #-}
16441743mergeCheapWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1645−mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy DMap.size
1744+mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
16461745 where
16471746 updateMe :: MergeUpdateFunc k x (PatchDMapWithMove k (Event x)) (MergeSubscribedParentWithMove x k)
16481747 updateMe subscriber heightBagRef oldParents p = do
@@ -1700,78 +1799,108 @@ type MergeDestroyFunc k s
17001799 = DMap k s
17011800 -> IO ()
17021801
1802+data Merge x k s = Merge
1803+ { _merge_parentsRef :: {-# UNPACK #-} !(IORef (DMap k s))
1804+ , _merge_heightBagRef :: {-# UNPACK #-} !(IORef HeightBag)
1805+ , _merge_heightRef :: {-# UNPACK #-} !(IORef Height)
1806+ , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k Identity))
1807+ , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k Identity))
1808+ }
1809+
1810+invalidateMergeHeight :: Merge x k s -> IO ()
1811+invalidateMergeHeight m = invalidateMergeHeight' (_merge_heightRef m) (_merge_sub m)
1812+
1813+invalidateMergeHeight' :: IORef Height -> Subscriber x a -> IO ()
1814+invalidateMergeHeight' heightRef sub = do
1815+ oldHeight <- readIORef heightRef
1816+ when (oldHeight /= invalidHeight) $ do -- If the height used to be valid, it must be invalid now; we should never have *more* heights than we have parents
1817+ writeIORef heightRef $! invalidHeight
1818+ subscriberInvalidateHeight sub oldHeight
1819+
1820+
1821+revalidateMergeHeight :: Merge x k s -> IO ()
1822+revalidateMergeHeight m = do
1823+ currentHeight <- readIORef $ _merge_heightRef m
1824+ when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
1825+ heights <- readIORef $ _merge_heightBagRef m
1826+ parents <- readIORef $ _merge_parentsRef m
1827+ -- When the number of heights in the bag reaches the number of parents, we should have a valid height
1828+ case heightBagSize heights `compare` DMap.size parents of
1829+ LT -> return ()
1830+ EQ -> do
1831+ let height = succHeight $ heightBagMax heights
1832+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1833+ writeIORef (_merge_heightRef m) $! height
1834+ subscriberRecalculateHeight (_merge_sub m) height
1835+ GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (DMap.size parents) <> ") for Merge"
1836+
1837+scheduleMergeSelf :: HasSpiderTimeline x => Merge x k s -> Height -> EventM x ()
1838+scheduleMergeSelf m height = scheduleMerge' height (_merge_heightRef m) $ do
1839+ vals <- liftIO $ readIORef $ _merge_accumRef m
1840+ liftIO $ writeIORef (_merge_accumRef m) $! DMap.empty -- Once we're done with this, we can clear it immediately, because if there's a cacheEvent in front of us, it'll handle subsequent subscribers, and if not, we won't get subsequent subscribers
1841+ --TODO: Assert that m is not empty
1842+ subscriberPropagate (_merge_sub m) vals
1843+
1844+mergeSubscriber :: forall x k s a. (HasSpiderTimeline x, GCompare k) => Merge x k s -> EventM x (k a) -> Subscriber x a
1845+mergeSubscriber m getKey = Subscriber
1846+ { subscriberPropagate = \a -> do
1847+ oldM <- liftIO $ readIORef $ _merge_accumRef m
1848+ k <- getKey
1849+ let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k (Identity a) oldM
1850+ tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1851+ liftIO $ writeIORef (_merge_accumRef m) $! newM
1852+ when (DMap.null oldM) $ do -- Only schedule the firing once
1853+ height <- liftIO $ readIORef $ _merge_heightRef m
1854+ --TODO: assertions about height
1855+ currentHeight <- getCurrentHeight
1856+ when (height <= currentHeight) $ do
1857+ if height /= invalidHeight
1858+ then do
1859+ myStack <- liftIO $ whoCreatedIORef undefined --TODO
1860+ error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")\n" ++ unlines (reverse myStack)
1861+ else liftIO $ do
1862+#ifdef DEBUG_CYCLES
1863+ nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1864+ stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1865+ let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
1866+#else
1867+ let cycleInfo = ""
1868+#endif
1869+ error $ "Causality loop found" <> cycleInfo
1870+ scheduleMergeSelf m height
1871+ , subscriberInvalidateHeight = \old -> do --TODO: When removing a parent doesn't actually change the height, maybe we can avoid invalidating
1872+ modifyIORef' (_merge_heightBagRef m) $ heightBagRemove old
1873+ invalidateMergeHeight m
1874+ , subscriberRecalculateHeight = \new -> do
1875+ modifyIORef' (_merge_heightBagRef m) $ heightBagAdd new
1876+ revalidateMergeHeight m
1877+ }
1878+
1879+--TODO: Be able to run as much of this as possible promptly
1880+updateMerge :: (HasSpiderTimeline x, GCompare k) => Merge x k s -> MergeUpdateFunc k x p s -> p -> SomeMergeUpdate x
1881+updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m) (revalidateMergeHeight m)
1882+ where updateMe = do
1883+ oldParents <- liftIO $ readIORef $ _merge_parentsRef m
1884+ (subscriptionsToKill, newParents) <- updateFunc (mergeSubscriber m) (_merge_heightBagRef m) oldParents p
1885+ liftIO $ writeIORef (_merge_parentsRef m) $! newParents
1886+ return subscriptionsToKill
1887+
17031888{-# INLINE mergeCheap' #-}
1704−mergeCheap' :: forall k x p s. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k (Event x)) => MergeInitFunc k x s -> MergeUpdateFunc k x p s -> MergeDestroyFunc k s -> (DMap k s -> Int) -> Dynamic x p -> Event x (DMap k Identity)
1705−mergeCheap' getInitialSubscribers updateFunc destroy numParents d = Event $ \sub -> do
1889+mergeCheap' :: forall k x p s. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k (Event x)) => MergeInitFunc k x s -> MergeUpdateFunc k x p s -> MergeDestroyFunc k s -> Dynamic x p -> Event x (DMap k Identity)
1890+mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
17061891 initialParents <- readBehaviorUntracked $ dynamicCurrent d
17071892 accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
17081893 heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
17091894 heightBagRef <- liftIO $ newIORef $ error "merge: heightBagRef not yet initialized"
17101895 parentsRef :: IORef (DMap k s) <- liftIO $ newIORef $ error "merge: parentsRef not yet initialized"
1711− let scheduleSelf height = scheduleMerge' height heightRef $ do
1712− m <- liftIO $ readIORef accumRef
1713− liftIO $ writeIORef accumRef $! DMap.empty
1714− --TODO: Assert that m is not empty
1715− subscriberPropagate sub m
1716− invalidateMyHeight = do
1717− oldHeight <- readIORef heightRef
1718− when (oldHeight /= invalidHeight) $ do -- If the height used to be valid, it must be invalid now; we should never have *more* heights than we have parents
1719− writeIORef heightRef $! invalidHeight
1720− subscriberInvalidateHeight sub oldHeight
1721− revalidateMyHeight = do
1722− currentHeight <- readIORef heightRef
1723− when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
1724− heights <- readIORef heightBagRef
1725− parents <- readIORef parentsRef
1726− -- When the number of heights in the bag reaches the number of parents, we should have a valid height
1727− case heightBagSize heights `compare` numParents parents of
1728− LT -> return ()
1729− EQ -> do
1730− let height = succHeight $ heightBagMax heights
1731− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1732− writeIORef heightRef $! height
1733− subscriberRecalculateHeight sub height
1734− GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (numParents parents) <> ") for Merge"
1735− subscriber :: forall a. EventM x (k a) -> Subscriber x a
1736− subscriber getKey = Subscriber
1737− { subscriberPropagate = \a -> do
1738− oldM <- liftIO $ readIORef accumRef
1739− k <- getKey
1740− let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k (Identity a) oldM
1741− tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1742− liftIO $ writeIORef accumRef $! newM
1743− when (DMap.null oldM) $ do -- Only schedule the firing once
1744− height <- liftIO $ readIORef heightRef
1745− --TODO: assertions about height
1746− currentHeight <- getCurrentHeight
1747− when (height <= currentHeight) $ do
1748− if height /= invalidHeight
1749− then do
1750− myStack <- liftIO $ whoCreatedIORef undefined --TODO
1751− error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")\n" ++ unlines (reverse myStack)
1752− else liftIO $ do
1753−#ifdef DEBUG_CYCLES
1754− nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1755− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1756− let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
1757−#else
1758− let cycleInfo = ""
1759−#endif
1760− error $ "Causality loop found" <> cycleInfo
1761− scheduleSelf height
1762− , subscriberInvalidateHeight = \old -> do --TODO: When removing a parent doesn't actually change the height, maybe we can avoid invalidating
1763− modifyIORef' heightBagRef $ heightBagRemove old
1764− invalidateMyHeight
1765− , subscriberRecalculateHeight = \new -> do
1766− modifyIORef' heightBagRef $ heightBagAdd new
1767− revalidateMyHeight
1896+ let m = Merge
1897+ { _merge_parentsRef = parentsRef
1898+ , _merge_heightBagRef = heightBagRef
1899+ , _merge_heightRef = heightRef
1900+ , _merge_sub = sub
1901+ , _merge_accumRef = accumRef
17681902 }
1769− let updateMe x = do
1770− oldParents <- liftIO $ readIORef parentsRef
1771− (subscriptionsToKill, newParents) <- updateFunc subscriber heightBagRef oldParents x
1772− liftIO $ writeIORef parentsRef $! newParents
1773− return subscriptionsToKill
1774− (dm, heights, initialParentState) <- getInitialSubscribers initialParents subscriber
1903+ (dm, heights, initialParentState) <- getInitialSubscribers initialParents $ mergeSubscriber m
17751904 let myHeightBag = heightBagFromList $ filter (/= invalidHeight) heights
17761905 myHeight = if invalidHeight `elem` heights
17771906 then invalidHeight
@@ -1781,9 +1910,7 @@ mergeCheap' getInitialSubscribers updateFunc destroy numParents d = Event $ \sub
17811910 then (if DMap.null dm then Nothing else Just dm, DMap.empty)
17821911 else (Nothing, dm)
17831912 unless (DMap.null accum) $ do
1784− scheduleSelf myHeight
1785− occRef <- liftIO $ newIORef occ
1786− when (isJust occ) $ scheduleClear occRef
1913+ scheduleMergeSelf m myHeight
17871914 liftIO $ writeIORef accumRef $! accum
17881915 liftIO $ writeIORef heightRef $! myHeight
17891916 liftIO $ writeIORef heightBagRef $! myHeightBag
@@ -1793,12 +1920,12 @@ mergeCheap' getInitialSubscribers updateFunc destroy numParents d = Event $ \sub
17931920 let s = Subscriber
17941921 { subscriberPropagate = \a -> {-# SCC "traverseMergeChange" #-} do
17951922 tracePropagate (Proxy :: Proxy x) $ "SubscriberMerge/Change"
1796− defer $ SomeMergeUpdate (updateMe a) invalidateMyHeight revalidateMyHeight
1923+ defer $ updateMerge m updateFunc a
17971924 , subscriberInvalidateHeight = \_ -> return ()
17981925 , subscriberRecalculateHeight = \_ -> return ()
17991926 }
18001927 (changeSubscription, change) <- subscribeAndRead (dynamicUpdated d) s
1801− forM_ change $ \c -> defer $ SomeMergeUpdate (updateMe c) invalidateMyHeight revalidateMyHeight
1928+ forM_ change $ \c -> defer $ updateMerge m updateFunc c
18021929 -- We explicitly hold on to the unsubscribe function from subscribing to the update event.
18031930 -- If we don't do this, there are certain cases where mergeCheap will fail to properly retain
18041931 -- its subscription.
@@ -1808,6 +1935,106 @@ mergeCheap' getInitialSubscribers updateFunc destroy numParents d = Event $ \sub
18081935 , occ
18091936 )
18101937
1938+mergeInt :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1939+mergeInt = cacheEvent . mergeIntCheap
1940+
1941+{-# INLINABLE mergeIntCheap #-}
1942+mergeIntCheap :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1943+mergeIntCheap d = Event $ \sub -> do
1944+ initialParents <- readBehaviorUntracked $ dynamicCurrent d
1945+ accum <- liftIO $ FastMutableIntMap.newEmpty
1946+ heightRef <- liftIO $ newIORef zeroHeight
1947+ heightBagRef <- liftIO $ newIORef heightBagEmpty
1948+ parents <- liftIO $ FastMutableIntMap.newEmpty
1949+ let scheduleSelf = do
1950+ height <- liftIO $ readIORef $ heightRef
1951+ scheduleMerge' height heightRef $ do
1952+ vals <- liftIO $ FastMutableIntMap.getFrozenAndClear accum
1953+ subscriberPropagate sub vals
1954+ invalidateMyHeight = do
1955+ invalidateMergeHeight' heightRef sub
1956+ recalculateMyHeight = do
1957+ currentHeight <- readIORef heightRef
1958+ when (currentHeight == invalidHeight) $ do --TODO: This will almost always be true; can we get rid of this check and just proceed to the next one always?
1959+ heights <- readIORef heightBagRef
1960+ numParents <- FastMutableIntMap.size parents
1961+ case heightBagSize heights `compare` numParents of
1962+ LT -> return ()
1963+ EQ -> do
1964+ let height = succHeight $ heightBagMax heights
1965+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1966+ writeIORef heightRef $! height
1967+ subscriberRecalculateHeight sub height
1968+ GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show numParents <> ") for Merge"
1969+ mySubscriber k = Subscriber
1970+ { subscriberPropagate = \a -> do
1971+ wasEmpty <- liftIO $ FastMutableIntMap.isEmpty accum
1972+ liftIO $ FastMutableIntMap.insert accum k a
1973+ when wasEmpty scheduleSelf
1974+ , subscriberInvalidateHeight = \old -> do
1975+ modifyIORef' heightBagRef $ heightBagRemove old
1976+ invalidateMyHeight
1977+ , subscriberRecalculateHeight = \new -> do
1978+ modifyIORef' heightBagRef $ heightBagAdd new
1979+ recalculateMyHeight
1980+ }
1981+ forM_ (IntMap.toList initialParents) $ \(k, p) -> do
1982+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead p $ mySubscriber k
1983+ liftIO $ do
1984+ forM_ parentOcc $ FastMutableIntMap.insert accum k
1985+ FastMutableIntMap.insert parents k subscription
1986+ height <- getEventSubscribedHeight parentSubd
1987+ if height == invalidHeight
1988+ then writeIORef heightRef invalidHeight
1989+ else do
1990+ modifyIORef' heightBagRef $ heightBagAdd height
1991+ modifyIORef' heightRef $ \oldHeight ->
1992+ if oldHeight == invalidHeight
1993+ then invalidHeight
1994+ else max (succHeight height) oldHeight
1995+ myHeight <- liftIO $ readIORef heightRef
1996+ currentHeight <- getCurrentHeight
1997+ isEmpty <- liftIO $ FastMutableIntMap.isEmpty accum
1998+ occ <- if currentHeight >= myHeight -- If we should have fired by now
1999+ then if isEmpty
2000+ then return Nothing
2001+ else liftIO $ Just <$> FastMutableIntMap.getFrozenAndClear accum
2002+ else do when (not isEmpty) scheduleSelf -- We have things accumulated, but we shouldn't have fired them yet
2003+ return Nothing
2004+ changeSubdRef <- liftIO $ newIORef $ error "getMergeSubscribed: changeSubdRef not yet initialized"
2005+ defer $ SomeMergeInit $ do
2006+ let updateMe a = SomeMergeUpdate u invalidateMyHeight recalculateMyHeight
2007+ where
2008+ u = do
2009+ let f k newParent = do
2010+ subscription@(EventSubscription _ subd) <- subscribe newParent $ mySubscriber k
2011+ newParentHeight <- liftIO $ getEventSubscribedHeight subd
2012+ liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
2013+ return subscription
2014+ newSubscriptions <- FastMutableIntMap.traverseIntMapPatchWithKey f a
2015+ oldParents <- liftIO $ FastMutableIntMap.applyPatch parents newSubscriptions
2016+ liftIO $ for_ oldParents $ \oldParent -> do
2017+ oldParentHeight <- getEventSubscribedHeight $ _eventSubscription_subscribed oldParent
2018+ modifyIORef' heightBagRef $ heightBagRemove oldParentHeight
2019+ return $ IntMap.elems oldParents
2020+ let s = Subscriber
2021+ { subscriberPropagate = \a -> {-# SCC "traverseMergeChange" #-} do
2022+ tracePropagate (Proxy :: Proxy x) $ "SubscriberMergeInt/Change"
2023+ defer $ updateMe a
2024+ , subscriberInvalidateHeight = \_ -> return ()
2025+ , subscriberRecalculateHeight = \_ -> return ()
2026+ }
2027+ (changeSubscription, change) <- subscribeAndRead (dynamicUpdated d) s
2028+ forM_ change $ \c -> defer $ updateMe c
2029+ -- We explicitly hold on to the unsubscribe function from subscribing to the update event.
2030+ -- If we don't do this, there are certain cases where mergeCheap will fail to properly retain
2031+ -- its subscription.
2032+ liftIO $ writeIORef changeSubdRef (s, changeSubscription)
2033+ let unsubscribeAll = traverse_ unsubscribe =<< FastMutableIntMap.getFrozenAndClear parents
2034+ return ( EventSubscription unsubscribeAll $ EventSubscribed heightRef $ toAny (parents, changeSubdRef)
2035+ , occ
2036+ )
2037+
18112038newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
18122039
18132040fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
@@ -1847,13 +2074,14 @@ newEventEnv = do
18472074 mergeInitRef <- newIORef []
18482075 heightRef <- newIORef zeroHeight
18492076 toClearRef <- newIORef []
2077+ toClearIntRef <- newIORef []
18502078 toClearRootRef <- newIORef []
18512079 coincidenceInfosRef <- newIORef []
18522080 delayedRef <- newIORef IntMap.empty
1853− return $ EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef
2081+ return $ EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeInitRef toClearRef toClearIntRef toClearRootRef heightRef coincidenceInfosRef delayedRef
18542082
18552083clearEventEnv :: EventEnv x -> IO ()
1856−clearEventEnv (EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef) = do
2084+clearEventEnv (EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeInitRef toClearRef toClearIntRef toClearRootRef heightRef coincidenceInfosRef delayedRef) = do
18572085 writeIORef toAssignRef []
18582086 writeIORef holdInitRef []
18592087 writeIORef dynInitRef []
@@ -1861,6 +2089,7 @@ clearEventEnv (EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeI
18612089 writeIORef mergeInitRef []
18622090 writeIORef heightRef zeroHeight
18632091 writeIORef toClearRef []
2092+ writeIORef toClearIntRef []
18642093 writeIORef toClearRootRef []
18652094 writeIORef coincidenceInfosRef []
18662095 writeIORef delayedRef IntMap.empty
@@ -1876,6 +2105,8 @@ runFrame a = SpiderHost $ ask >>= \_ -> lift $ do
18762105 result <- runEventM go
18772106 toClear <- readIORef $ eventEnvClears env
18782107 forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
2108+ toClearInt <- readIORef $ eventEnvIntClears env
2109+ forM_ toClearInt $ \(SomeIntClear ref) -> {-# SCC "intClear" #-} writeIORef ref $! IntMap.empty
18792110 toClearRoot <- readIORef $ eventEnvRootClears env
18802111 forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
18812112 toAssign <- readIORef $ eventEnvAssignments env
@@ -2070,6 +2301,8 @@ withSpiderTimeline k = do
20702301
20712302newtype SpiderPullM x a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
20722303
2304+type ComputeM = EventM
2305+
20732306newtype SpiderPushM x a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
20742307
20752308#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
@@ -2122,6 +2355,10 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
21222355 eventCoercion Coercion = Coercion
21232356 behaviorCoercion Coercion = Coercion
21242357 dynamicCoercion = unsafeCoerce --TODO: How can we avoid this unsafeCoerce? This is safe only because we know how Identity works as a Patch instance
2358+ {-# INLINABLE mergeIntIncremental #-}
2359+ mergeIntIncremental = SpiderEvent . mergeInt . (unsafeCoerce :: Dynamic x (PatchIntMap (R.Event (SpiderTimeline x) a)) -> Dynamic x (PatchIntMap (Event x a))) . unSpiderIncremental
2360+ {-# INLINABLE fanInt #-}
2361+ fanInt e = R.EventSelectorInt $ SpiderEvent . selectInt (fanInt (unSpiderEvent e))
21252362#endif
21262363
21272364data RootTrigger x a = forall k. GCompare k => RootTrigger (WeakBag (Subscriber x a), IORef (DMap k Identity), k a)