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)