From e9c75d68dffde2dc7af333ede0e716c2e60ccb66 Mon Sep 17 00:00:00 2001 From: Jens Nolte <jens@nightmarestudio.de> Date: Wed, 18 Mar 2020 02:40:30 +0100 Subject: [PATCH] Invalidate SignalBlocks on click --- src/QBar/BlockHelper.hs | 84 ++++++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 27 deletions(-) diff --git a/src/QBar/BlockHelper.hs b/src/QBar/BlockHelper.hs index 7a7ab19..7bd5983 100644 --- a/src/QBar/BlockHelper.hs +++ b/src/QBar/BlockHelper.hs @@ -9,11 +9,14 @@ import QBar.Time import Control.Concurrent.Async import qualified Control.Concurrent.Event as Event import Control.Concurrent.STM.TChan +import Control.Concurrent.STM.TVar +import Control.Monad.Reader (ReaderT) import Control.Monad.State (StateT, evalStateT, get, put) import Data.Either (isRight) import Pipes import Pipes.Concurrent import Pipes.Core +import Pipes.Safe (bracket, runSafeT) data Signal a = RegularSignal | UserSignal a | EventSignal BlockEvent deriving (Show, Eq) @@ -90,36 +93,41 @@ runSignalBlockConfiguration SignalBlockConfiguration{aquire, release, signalThre -- Initialize signalChan <- liftIO newTChanIO signalEvent <- liftIO Event.new + isInvalidatedVar <- liftIO $ newTVarIO False - runSignalBlockWithThreadInternal signalChan signalEvent + runSignalBlockWithThreadInternal signalChan signalEvent isInvalidatedVar where - runSignalBlockWithThreadInternal :: TChan (Signal p) -> Event.Event -> Block - runSignalBlockWithThreadInternal signalChan signalEvent = do - context <- lift $ aquire userSignalAction - -- Start signalSource thread - userTask <- liftBarIO $ barAsync $ - case signalThread of - Just signalThread' -> signalThread' context userSignalAction - Nothing -> return () - intervalTask <- liftBarIO $ barAsync intervalTimer - - -- Run block - void (signalBlock context +>> signalPipe) - - -- Cancel threads when the block terminates - -- TODO: use bracketP? - liftIO $ do - cancel userTask - cancel intervalTask - - liftBarIO $ release context - + runSignalBlockWithThreadInternal :: TChan (Signal p) -> Event.Event -> TVar Bool -> Block + runSignalBlockWithThreadInternal signalChan signalEvent isInvalidatedVar = do + bracket aquire' release' (\(context, _, _) -> void (signalBlock context +>> signalPipe)) exitBlock - where + aquire' :: ReaderT Bar IO (c, Async (), Async ()) + aquire' = runSafeT $ do + context <- aquire userSignalAction + + -- Start signalSource thread + userTask <- barAsync $ + case signalThread of + Just signalThread' -> signalThread' context userSignalAction + Nothing -> return () + intervalTask <- barAsync intervalTimer + + return (context, userTask, intervalTask) + + + release' :: (c, Async (), Async ()) -> ReaderT Bar IO () + release' (context, userTask, intervalTask) = do + liftIO $ do + cancel userTask + cancel intervalTask + + runSafeT $ release context + + userSignalAction :: p -> IO () userSignalAction value = do - liftIO . atomically $ writeTChan signalChan $ UserSignal value + atomically $ writeTChan signalChan $ UserSignal value Event.set signalEvent signalPipe :: Proxy (Signal p) (Maybe BlockOutput) () BlockUpdate BarIO ExitBlock @@ -164,7 +172,19 @@ runSignalBlockConfiguration SignalBlockConfiguration{aquire, release, signalThre outputAndStore :: Signal p -> StateT BlockUpdate (Proxy (Signal p) (Maybe BlockOutput) () BlockUpdate BarIO) () outputAndStore signal = do maybeOutput <- lift $ request signal - let update = (mkBlockStateWithHandler maybeOutput, signalToReason signal) + + invalidate <- if isEventSignal signal + then do + -- Reset invalidate flag + liftIO . atomically $ writeTVar isInvalidatedVar False + return False + else + liftIO . atomically $ readTVar isInvalidatedVar + + let state = mkBlockStateWithHandler maybeOutput + let state' = if invalidate then invalidateBlockState state else state + + let update = (state', signalToReason signal) put update lift $ yield update @@ -173,6 +193,10 @@ runSignalBlockConfiguration SignalBlockConfiguration{aquire, release, signalThre signalToReason (EventSignal _) = EventUpdate signalToReason RegularSignal = PollUpdate + isEventSignal :: Signal p -> Bool + isEventSignal (EventSignal _) = True + isEventSignal _ = False + intervalTimer :: BarIO () intervalTimer = do @@ -185,8 +209,14 @@ runSignalBlockConfiguration SignalBlockConfiguration{aquire, release, signalThre signalEventHandler :: BlockEventHandler signalEventHandler event = do - liftIO . atomically $ writeTChan signalChan $ EventSignal event - liftIO $ Event.set signalEvent + wasInvalidated' <- liftIO . atomically $ do + wasInvalidated <- readTVar isInvalidatedVar + unless wasInvalidated $ do + writeTChan signalChan $ EventSignal event + writeTVar isInvalidatedVar True + return wasInvalidated + + unless wasInvalidated' $ liftIO $ Event.set signalEvent -- |Converts a 'PollBlock' to a 'Block' by running it whenever the 'defaultInterval' is triggered. -- GitLab