@@ -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 #-}
@@ -62,6 +60,7 @@ import Data.Word
6260import GHC.Exts
6361import GHC.IORef (IORef (..))
6462import GHC.Stack
63+import Reflex.FastWeak
6564import Reflex.FunctorMaybe
6665import System.IO.Unsafe
6766import System.Mem.Weak
@@ -75,6 +74,8 @@ import Data.Monoid (mempty)
7574import Data.Tree (Forest, Tree (..), drawForest)
7675#endif
7776
77+import Data.FastWeakBag (FastWeakBag)
78+import qualified Data.FastWeakBag as FastWeakBag
7879import Data.WeakBag (WeakBag, WeakBagTicket, _weakBag_children)
7980import qualified Data.WeakBag as WeakBag
8081#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
@@ -252,10 +253,9 @@ pushCheap f e = Event $ \sub -> do
252253 return (subscription, occ')
253254
254255data CacheSubscribed x a
255− = CacheSubscribed { _cacheSubscribed_subscribers :: {-# UNPACK #-} !(WeakBag (Subscriber x a))
256− , _cacheSubscribed_weakSelf :: {-# UNPACK #-} !(IORef (Weak (CacheSubscribed x a)))
256+ = CacheSubscribed { _cacheSubscribed_subscribers :: {-# UNPACK #-} !(FastWeakBag (Subscriber x a))
257257 , _cacheSubscribed_parent :: {-# UNPACK #-} !(EventSubscription x)
258− , _cacheSubscribed_cached :: {-# UNPACK #-} !(IORef (Maybe (CacheSubscribed x a)))
258+ , _cacheSubscribed_cached :: {-# UNPACK #-} !(IORef (Maybe (FastWeakTicket (CacheSubscribed x a))))
259259 , _cacheSubscribed_occurrence :: {-# UNPACK #-} !(IORef (Maybe a))
260260 }
261261
@@ -263,57 +263,59 @@ data CacheSubscribed x a
263263-- repeatedly
264264--
265265--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
266+--there's only one subscriber, and then build our own FastWeakBag only when a second
267267--subscriber joins
268268{-# NOINLINE cacheEvent #-}
269−cacheEvent :: HasSpiderTimeline x => Event x a -> Event x a
269+cacheEvent :: forall x a. HasSpiderTimeline x => Event x a -> Event x a
270270cacheEvent e =
271271#ifdef DEBUG_TRACE_EVENTS
272272 withStackOneLine $ \callSite -> Event $
273273#else
274274 Event $
275275#endif
276− let !mSubscribedRef = unsafeNewIORef e Nothing
276+ let mSubscribedRef :: IORef (Maybe (FastWeakTicket (CacheSubscribed x a)))
277+ !mSubscribedRef = unsafeNewIORef e Nothing
277278 in \sub -> {-# SCC "cacheEvent" #-} do
278279#ifdef DEBUG_TRACE_EVENTS
279280 unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
280281#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)
282+ subscribedTicket <- liftIO (readIORef mSubscribedRef) >>= \case
283+ Just subscribedTicket -> return subscribedTicket
291284 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
285+ subscribers <- liftIO FastWeakBag.empty
286+ occRef <- liftIO $ newIORef Nothing -- This should never be read prior to being set below
295287 (parentSub, occ) <- subscribeAndRead e $ Subscriber
296288 { subscriberPropagate = \a -> do
297289 liftIO $ writeIORef occRef $ Just a
298290 scheduleClear occRef
299− propagate a subscribers
291+ propagateFast a subscribers
300292 , subscriberInvalidateHeight = \old -> do
301− WeakBag.traverse subscribers $ invalidateSubscriberHeight old
293+ FastWeakBag.traverse subscribers $ invalidateSubscriberHeight old
302294 , subscriberRecalculateHeight = \new -> do
303− WeakBag.traverse subscribers $ recalculateSubscriberHeight new
295+ FastWeakBag.traverse subscribers $ recalculateSubscriberHeight new
304296 }
305− liftIO $ writeIORef occRef occ
306− when (isJust occ) $ scheduleClear occRef
297+ when (isJust occ) $ do
298+ liftIO $ writeIORef occRef occ -- Set the initial value of occRef; we don't need to do this if occ is Nothing
299+ scheduleClear occRef
307300 let !subscribed = CacheSubscribed
308301 { _cacheSubscribed_subscribers = subscribers
309− , _cacheSubscribed_weakSelf = weakSelf
310302 , _cacheSubscribed_parent = parentSub
311303 , _cacheSubscribed_cached = mSubscribedRef
312304 , _cacheSubscribed_occurrence = occRef
313305 }
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)
306+ !subscribedTicket = fastWeakTicket subscribed
307+ liftIO $ writeIORef mSubscribedRef $ Just subscribedTicket
308+ return subscribedTicket
309+ liftIO $ do
310+ let subscribed = fastWeakTicketValue subscribedTicket
311+ subscribedWeak <- getFastWeakTicketWeak subscribedTicket
312+ ticket <- FastWeakBag.insert sub (_cacheSubscribed_subscribers subscribed) subscribedWeak cleanupCacheSubscribed
313+ occ <- readIORef $ _cacheSubscribed_occurrence subscribed
314+ let es = EventSubscribed
315+ { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ _cacheSubscribed_parent subscribed
316+ , eventSubscribedRetained = toAny subscribed
317+ }
318+ return (EventSubscription (FastWeakBag.remove ticket >> touch ticket) es, occ)
317319
318320cleanupCacheSubscribed :: CacheSubscribed x a -> IO ()
319321cleanupCacheSubscribed self = do
@@ -497,6 +499,13 @@ propagate a subscribers = withIncreasedDepth $ do
497499 --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
498500 WeakBag.traverse subscribers $ \s -> subscriberPropagate s a
499501
502+-- | Propagate everything at the current height
503+propagateFast :: a -> FastWeakBag (Subscriber x a) -> EventM x ()
504+propagateFast a subscribers = withIncreasedDepth $ do
505+ -- 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
506+ --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
507+ FastWeakBag.traverse subscribers $ \s -> subscriberPropagate s a
508+
500509--------------------------------------------------------------------------------
501510-- EventSubscribed
502511--------------------------------------------------------------------------------