glazier-react-1.0.0.0: src/Glazier/React/Subject/Internal.hs
module Glazier.React.Subject.Internal where
import Control.Concurrent
import Control.Monad
import Data.IORef
import Glazier.React.Scene
----------------------------------------------------------------------------------
-- | Using MVar for synchronizing because it guarantees FIFO wakeup
-- which will help prevent old updates overriding new updates.
-- The constructor is hidden to maintain the expectation that
-- the last two member will keep the 'ShimCallbacks' inside the 'Scene' alive.
-- Before removing Subject from a container, use 'prolong' to store
-- the prolonging IO action "nextRenderedListener" action handler,
-- so that the 'ShimCallbacks' is garbage collected
-- only after the child widget is no longer rendered.
data Subject p = Subject
(IORef (Scene p))
(MVar (Scene p))
(IORef (MVar ())) -- garbage collection for rendering callback
(MVar ()) -- garbage collection for other callbacks
deriving Eq
sceneRef :: Subject p -> IORef (Scene p)
sceneRef (Subject x _ _ _) = x
sceneVar :: Subject p -> MVar (Scene p)
sceneVar (Subject _ x _ _) = x
renderLeaseRef :: Subject p -> IORef (MVar ())
renderLeaseRef (Subject _ _ x _) = x
otherLeaseVar :: Subject p -> MVar ()
otherLeaseVar (Subject _ _ _ x) = x
-- | Creates an IO action whose existence will keep the lease alive.
-- Running it has no side effects.
prolong :: Subject s -> IO ()
prolong (Subject _ _ rndrLeaseRef otherCbLease) = do
rndrLease <- readIORef rndrLeaseRef
(void $ isEmptyMVar rndrLease)
(void $ isEmptyMVar otherCbLease)