@@ -26,7 +26,7 @@
27-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
27-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
28-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
28-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
29−module Reflex.Spider.Internal where
29+module Reflex.Spider.Internal (module Reflex.Spider.Internal) where
31import Control.Applicative
31import Control.Applicative
32import Control.Concurrent
32import Control.Concurrent
@@ -89,6 +89,20 @@ import Reflex.Patch
89-- Note: must come last to silence warnings due to AMP on GHC < 7.10
89-- Note: must come last to silence warnings due to AMP on GHC < 7.10
90import Prelude hiding (any, concat, mapM, mapM_, sequence)
90import Prelude hiding (any, concat, mapM, mapM_, sequence)
92+#ifdef DEBUG_TRACE_EVENTS
93+import System.IO (stderr)
94+import qualified Data.ByteString.Char8 as BS8
97+#ifdef DEBUG_TRACE_EVENTS
99+withStackOneLine :: (BS8.ByteString -> a) -> a
100+withStackOneLine expr = unsafePerformIO $ do
101+ stack <- currentCallStack
102+ return (expr (BS8.pack (unwords (reverse stack))))
92debugPropagate :: Bool
106debugPropagate :: Bool
94debugInvalidateHeight :: Bool
108debugInvalidateHeight :: Bool
@@ -190,6 +204,20 @@ unsafeNodeId a = unsafePerformIO $ do
190 newNodeId
204 newNodeId
207+--------------------------------------------------------------------------------
209+--------------------------------------------------------------------------------
211+--NB: Once you subscribe to an Event, you must always hold on the the WHOLE EventSubscription you get back
212+-- If you do not retain the subscription, you may be prematurely unsubscribed from the parent event.
213+data EventSubscription x = EventSubscription
214+ { _eventSubscription_unsubscribe :: !(IO ())
215+ , _eventSubscription_subscribed :: {-# UNPACK #-} !(EventSubscribed x)
218+unsubscribe :: EventSubscription x -> IO ()
219+unsubscribe (EventSubscription u _) = u
193--------------------------------------------------------------------------------
221--------------------------------------------------------------------------------
195--------------------------------------------------------------------------------
223--------------------------------------------------------------------------------
@@ -236,44 +264,53 @@ data CacheSubscribed x a
236--subscriber joins
264--subscriber joins
237{-# NOINLINE cacheEvent #-}
265{-# NOINLINE cacheEvent #-}
238cacheEvent :: HasSpiderTimeline x => Event x a -> Event x a
266cacheEvent :: HasSpiderTimeline x => Event x a -> Event x a
268+#ifdef DEBUG_TRACE_EVENTS
269+ withStackOneLine $ \callSite -> Event $
273+ let !mSubscribedRef = unsafeNewIORef e Nothing
239−cacheEvent e = Event $ \sub -> {-# SCC "cacheEvent" #-} do
274+ in \sub -> {-# SCC "cacheEvent" #-} do
240− let !mSubscribedRef = unsafeNewIORef e Nothing
275+#ifdef DEBUG_TRACE_EVENTS
276+ unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
241− mSubscribed <- liftIO $ readIORef mSubscribedRef
278+ mSubscribed <- liftIO $ readIORef mSubscribedRef
242− case mSubscribed of
279+ case mSubscribed of
243− Just subscribed -> liftIO $ do
280+ Just subscribed -> liftIO $ do
244− ticket <- WeakBag.insert sub (_cacheSubscribed_subscribers subscribed) (_cacheSubscribed_weakSelf subscribed) cleanupCacheSubscribed
281+ ticket <- WeakBag.insert sub (_cacheSubscribed_subscribers subscribed) (_cacheSubscribed_weakSelf subscribed) cleanupCacheSubscribed
245− occ <- readIORef $ _cacheSubscribed_occurrence subscribed
282+ occ <- readIORef $ _cacheSubscribed_occurrence subscribed
246− let es = EventSubscribed
283+ let es = EventSubscribed
247− { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ _cacheSubscribed_parent subscribed
284+ { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ _cacheSubscribed_parent subscribed
248− , eventSubscribedRetained = toAny subscribed
285+ , eventSubscribedRetained = toAny subscribed
250− return (EventSubscription (WeakBag.remove ticket >> touch ticket) es, occ)
287+ return (EventSubscription (WeakBag.remove ticket >> touch ticket) es, occ)
251− Nothing -> do
288+ Nothing -> do
252− weakSelf <- liftIO $ newIORef $ error "cacheEvent: weakSelf not yet intialized"
289+ weakSelf <- liftIO $ newIORef $ error "cacheEvent: weakSelf not yet intialized"
253− (subscribers, ticket) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCacheSubscribed
290+ (subscribers, ticket) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCacheSubscribed
254− occRef <- liftIO $ newIORef undefined
291+ occRef <- liftIO $ newIORef undefined
255− (parentSub, occ) <- subscribeAndRead e $ Subscriber
292+ (parentSub, occ) <- subscribeAndRead e $ Subscriber
256− { subscriberPropagate = \a -> do
293+ { subscriberPropagate = \a -> do
257− liftIO $ writeIORef occRef $ Just a
294+ liftIO $ writeIORef occRef $ Just a
258− scheduleClear occRef
295+ scheduleClear occRef
259− propagate a subscribers
296+ propagate a subscribers
260− , subscriberInvalidateHeight = \old -> do
297+ , subscriberInvalidateHeight = \old -> do
261− WeakBag.traverse subscribers $ invalidateSubscriberHeight old
298+ WeakBag.traverse subscribers $ invalidateSubscriberHeight old
262− , subscriberRecalculateHeight = \new -> do
299+ , subscriberRecalculateHeight = \new -> do
263− WeakBag.traverse subscribers $ recalculateSubscriberHeight new
300+ WeakBag.traverse subscribers $ recalculateSubscriberHeight new
265− liftIO $ writeIORef occRef occ
302+ liftIO $ writeIORef occRef occ
266− when (isJust occ) $ scheduleClear occRef
303+ when (isJust occ) $ scheduleClear occRef
267− let !subscribed = CacheSubscribed
304+ let !subscribed = CacheSubscribed
268− { _cacheSubscribed_subscribers = subscribers
305+ { _cacheSubscribed_subscribers = subscribers
269− , _cacheSubscribed_weakSelf = weakSelf
306+ , _cacheSubscribed_weakSelf = weakSelf
270− , _cacheSubscribed_parent = parentSub
307+ , _cacheSubscribed_parent = parentSub
271− , _cacheSubscribed_cached = mSubscribedRef
308+ , _cacheSubscribed_cached = mSubscribedRef
272− , _cacheSubscribed_occurrence = occRef
309+ , _cacheSubscribed_occurrence = occRef
274− liftIO $ writeIORef mSubscribedRef $ Just subscribed
311+ liftIO $ writeIORef mSubscribedRef $ Just subscribed
275− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "PushSubscribed"
312+ liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "PushSubscribed"
276− return (EventSubscription (WeakBag.remove ticket >> touch ticket) (EventSubscribed (eventSubscribedHeightRef $ _eventSubscription_subscribed parentSub) (toAny subscribed)), occ)
313+ return (EventSubscription (WeakBag.remove ticket >> touch ticket) (EventSubscribed (eventSubscribedHeightRef $ _eventSubscription_subscribed parentSub) (toAny subscribed)), occ)
278cleanupCacheSubscribed :: CacheSubscribed x a -> IO ()
315cleanupCacheSubscribed :: CacheSubscribed x a -> IO ()
279cleanupCacheSubscribed self = do
316cleanupCacheSubscribed self = do
@@ -558,18 +595,6 @@ walkInvalidHeightParents s0 = do
558subscribeHoldEvent :: CanSubscribe x m => Hold x p -> Subscriber x p -> m (EventSubscription x, Maybe p)
595subscribeHoldEvent :: CanSubscribe x m => Hold x p -> Subscriber x p -> m (EventSubscription x, Maybe p)
559subscribeHoldEvent = subscribeAndRead . holdEvent
596subscribeHoldEvent = subscribeAndRead . holdEvent
561−--------------------------------------------------------------------------------
563−--------------------------------------------------------------------------------
565−data EventSubscription x = EventSubscription
566− { _eventSubscription_unsubscribe :: !(IO ())
567− , _eventSubscription_subscribed :: {-# UNPACK #-} !(EventSubscribed x)
570−unsubscribe :: EventSubscription x -> IO ()
571−unsubscribe (EventSubscription u _) = u
573--------------------------------------------------------------------------------
598--------------------------------------------------------------------------------
574-- Behavior
599-- Behavior
575--------------------------------------------------------------------------------
600--------------------------------------------------------------------------------
@@ -1344,7 +1369,7 @@ getRootSubscribed k r sub = do
1344{-# ANN cleanupRootSubscribed "HLint: ignore Redundant bracket" #-}
1369{-# ANN cleanupRootSubscribed "HLint: ignore Redundant bracket" #-}
1346cleanupRootSubscribed :: RootSubscribed x a -> IO ()
1371cleanupRootSubscribed :: RootSubscribed x a -> IO ()
1347−cleanupRootSubscribed self@(RootSubscribed { rootSubscribedKey = k, rootSubscribedCachedSubscribed = cached }) = do
1372+cleanupRootSubscribed self@RootSubscribed { rootSubscribedKey = k, rootSubscribedCachedSubscribed = cached } = do
1348 rootSubscribedUninit self
1373 rootSubscribedUninit self
1349 modifyIORef' cached $ DMap.delete k
1374 modifyIORef' cached $ DMap.delete k
@@ -1641,9 +1666,12 @@ mergeCheap d = Event $ \sub -> do
1641 , subscriberInvalidateHeight = \_ -> return ()
1666 , subscriberInvalidateHeight = \_ -> return ()
1642 , subscriberRecalculateHeight = \_ -> return ()
1667 , subscriberRecalculateHeight = \_ -> return ()
1644− (EventSubscription _ changeSubd, change) <- subscribeAndRead (dynamicUpdated d) s
1669+ (changeSubscription, change) <- subscribeAndRead (dynamicUpdated d) s
1645 forM_ change $ \c -> defer $ SomeMergeUpdate (updateMe c) invalidateMyHeight revalidateMyHeight
1670 forM_ change $ \c -> defer $ SomeMergeUpdate (updateMe c) invalidateMyHeight revalidateMyHeight
1671+ -- We explicitly hold on to the unsubscribe function from subscribing to the update event.
1672+ -- If we don't do this, there are certain cases where mergeCheap will fail to properly retain
1673+ -- its subscription.
1646− liftIO $ writeIORef changeSubdRef (s, changeSubd)
1674+ liftIO $ writeIORef changeSubdRef (s, changeSubscription)
1647 let unsubscribeAll = do
1675 let unsubscribeAll = do
1648 parents <- readIORef parentsRef
1676 parents <- readIORef parentsRef
1649 forM_ (DMap.toList parents) $ \(_ :=> MergeSubscribedParent s) -> unsubscribe s
1677 forM_ (DMap.toList parents) $ \(_ :=> MergeSubscribedParent s) -> unsubscribe s
@@ -1922,6 +1950,8 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
1922 constant = SpiderBehavior . behaviorConst
1950 constant = SpiderBehavior . behaviorConst
1923 {-# INLINE push #-}
1951 {-# INLINE push #-}
1924 push f = SpiderEvent . push (coerce f) . unSpiderEvent
1952 push f = SpiderEvent . push (coerce f) . unSpiderEvent
1953+ {-# INLINE pushCheap #-}
1954+ pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
1925 {-# INLINABLE pull #-}
1955 {-# INLINABLE pull #-}
1926 pull = SpiderBehavior . pull . coerce
1956 pull = SpiderBehavior . pull . coerce
1927 {-# INLINABLE merge #-}
1957 {-# INLINABLE merge #-}
@@ -1935,7 +1965,7 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
1935 {-# INLINABLE current #-}
1965 {-# INLINABLE current #-}
1936 current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
1966 current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
1937 {-# INLINABLE updated #-}
1967 {-# INLINABLE updated #-}
1938− updated = SpiderEvent . fmap runIdentity . dynamicUpdated . unSpiderDynamic
1968+ updated = coerce $ SpiderEvent . dynamicUpdated . unSpiderDynamic
1939 {-# INLINABLE unsafeBuildDynamic #-}
1969 {-# INLINABLE unsafeBuildDynamic #-}
1940 unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (coerce readV0) $ coerce $ unSpiderEvent v'
1970 unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (coerce readV0) $ coerce $ unSpiderEvent v'
1941 {-# INLINABLE unsafeBuildIncremental #-}
1971 {-# INLINABLE unsafeBuildIncremental #-}