{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_HADDOCK not-home #-}
module UnliftIO.Debounce.Internal
( DebounceSettings (..)
, DebounceEdge (..)
, leadingEdge
, leadingMuteEdge
, trailingEdge
, trailingDelayEdge
, mkDebounceInternal
)
where
import Control.Monad (void, when)
import Control.Monad.IO.Class (liftIO)
import GHC.Clock (getMonotonicTimeNSec)
import GHC.Conc.Sync (labelThread)
import UnliftIO (MonadUnliftIO)
import UnliftIO.Concurrent (forkIO)
import UnliftIO.Exception (SomeException, handle, mask_)
import UnliftIO.MVar
( MVar
, newEmptyMVar
, putMVar
, tryPutMVar
, tryTakeMVar
)
import UnliftIO.STM (atomically, newTVarIO, readTVar, readTVarIO, writeTVar)
data DebounceSettings m = DebounceSettings
{ forall (m :: * -> *). DebounceSettings m -> Int
debounceFreq :: Int
, forall (m :: * -> *). DebounceSettings m -> m ()
debounceAction :: m ()
, forall (m :: * -> *). DebounceSettings m -> DebounceEdge
debounceEdge :: DebounceEdge
, forall (m :: * -> *). DebounceSettings m -> String
debounceThreadName :: String
}
data DebounceEdge
=
Leading
|
LeadingMute
|
Trailing
|
TrailingDelay
deriving (Int -> DebounceEdge -> ShowS
[DebounceEdge] -> ShowS
DebounceEdge -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [DebounceEdge] -> ShowS
$cshowList :: [DebounceEdge] -> ShowS
show :: DebounceEdge -> String
$cshow :: DebounceEdge -> String
showsPrec :: Int -> DebounceEdge -> ShowS
$cshowsPrec :: Int -> DebounceEdge -> ShowS
Show, DebounceEdge -> DebounceEdge -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: DebounceEdge -> DebounceEdge -> Bool
$c/= :: DebounceEdge -> DebounceEdge -> Bool
== :: DebounceEdge -> DebounceEdge -> Bool
$c== :: DebounceEdge -> DebounceEdge -> Bool
Eq)
leadingEdge :: DebounceEdge
leadingEdge :: DebounceEdge
leadingEdge = DebounceEdge
Leading
leadingMuteEdge :: DebounceEdge
leadingMuteEdge :: DebounceEdge
leadingMuteEdge = DebounceEdge
LeadingMute
trailingEdge :: DebounceEdge
trailingEdge :: DebounceEdge
trailingEdge = DebounceEdge
Trailing
trailingDelayEdge :: DebounceEdge
trailingDelayEdge :: DebounceEdge
trailingDelayEdge = DebounceEdge
TrailingDelay
mkDebounceInternal ::
forall m.
MonadUnliftIO m =>
MVar () ->
(Int -> m ()) ->
DebounceSettings m ->
m (m ())
mkDebounceInternal :: forall (m :: * -> *).
MonadUnliftIO m =>
MVar () -> (Int -> m ()) -> DebounceSettings m -> m (m ())
mkDebounceInternal MVar ()
baton Int -> m ()
delayFn (DebounceSettings Int
freq m ()
action DebounceEdge
edge String
name) =
case DebounceEdge
edge of
DebounceEdge
Leading -> MVar () -> m ()
leadingDebounce forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a. MonadIO m => m (MVar a)
newEmptyMVar
DebounceEdge
LeadingMute -> forall (f :: * -> *) a. Applicative f => a -> f a
pure m ()
leadingMuteDebounce
DebounceEdge
Trailing -> forall (f :: * -> *) a. Applicative f => a -> f a
pure m ()
trailingDebounce
DebounceEdge
TrailingDelay -> TVar Word64 -> m ()
trailingDelayDebounce forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a. MonadIO m => a -> m (TVar a)
newTVarIO forall a. Bounded a => a
minBound
where
leadingDebounce :: MVar () -> m ()
leadingDebounce MVar ()
trigger = do
Maybe ()
success <- forall (m :: * -> *) a. MonadIO m => MVar a -> m (Maybe a)
tryTakeMVar MVar ()
baton
case Maybe ()
success of
Maybe ()
Nothing -> forall (f :: * -> *) a. Functor f => f a -> f ()
void forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. MonadIO m => MVar a -> a -> m Bool
tryPutMVar MVar ()
trigger ()
Just () -> do
forall (f :: * -> *) a. Functor f => f a -> f ()
void forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. MonadIO m => MVar a -> m (Maybe a)
tryTakeMVar MVar ()
trigger
forall {m :: * -> *}. MonadUnliftIO m => m () -> m ()
forkAndLabel m ()
loop
where
loop :: m ()
loop = do
forall {m :: * -> *}. MonadUnliftIO m => m () -> m ()
ignoreExc m ()
action
Int -> m ()
delayFn Int
freq
Maybe ()
isTriggered <- forall (m :: * -> *) a. MonadIO m => MVar a -> m (Maybe a)
tryTakeMVar MVar ()
trigger
case Maybe ()
isTriggered of
Maybe ()
Nothing -> forall (m :: * -> *) a. MonadIO m => MVar a -> a -> m ()
putMVar MVar ()
baton ()
Just () -> m ()
loop
leadingMuteDebounce :: m ()
leadingMuteDebounce = do
Maybe ()
success <- forall (m :: * -> *) a. MonadIO m => MVar a -> m (Maybe a)
tryTakeMVar MVar ()
baton
case Maybe ()
success of
Maybe ()
Nothing -> forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Just () ->
forall {m :: * -> *}. MonadUnliftIO m => m () -> m ()
forkAndLabel forall a b. (a -> b) -> a -> b
$ do
forall {m :: * -> *}. MonadUnliftIO m => m () -> m ()
ignoreExc m ()
action
Int -> m ()
delayFn Int
freq
forall (m :: * -> *) a. MonadIO m => MVar a -> a -> m ()
putMVar MVar ()
baton ()
trailingDebounce :: m ()
trailingDebounce = do
Maybe ()
success <- forall (m :: * -> *) a. MonadIO m => MVar a -> m (Maybe a)
tryTakeMVar MVar ()
baton
case Maybe ()
success of
Maybe ()
Nothing -> forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Just () ->
forall {m :: * -> *}. MonadUnliftIO m => m () -> m ()
forkAndLabel forall a b. (a -> b) -> a -> b
$ do
Int -> m ()
delayFn Int
freq
forall {m :: * -> *}. MonadUnliftIO m => m () -> m ()
ignoreExc m ()
action
forall (m :: * -> *) a. MonadIO m => MVar a -> a -> m ()
putMVar MVar ()
baton ()
trailingDelayDebounce :: TVar Word64 -> m ()
trailingDelayDebounce TVar Word64
timeTVar = do
Word64
now <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO Word64
getMonotonicTimeNSec
Maybe ()
success <- forall (m :: * -> *) a. MonadIO m => MVar a -> m (Maybe a)
tryTakeMVar MVar ()
baton
case Maybe ()
success of
Maybe ()
Nothing -> forall (m :: * -> *) a. MonadIO m => STM a -> m a
atomically forall a b. (a -> b) -> a -> b
$ do
Word64
oldTime <- forall a. TVar a -> STM a
readTVar TVar Word64
timeTVar
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Word64
oldTime forall a. Ord a => a -> a -> Bool
< Word64
now) forall a b. (a -> b) -> a -> b
$ forall a. TVar a -> a -> STM ()
writeTVar TVar Word64
timeTVar Word64
now
Just () -> do
forall (m :: * -> *) a. MonadIO m => STM a -> m a
atomically forall a b. (a -> b) -> a -> b
$ forall a. TVar a -> a -> STM ()
writeTVar TVar Word64
timeTVar Word64
now
forall {m :: * -> *}. MonadUnliftIO m => m () -> m ()
forkAndLabel forall a b. (a -> b) -> a -> b
$ Int -> m ()
loop Int
freq
where
loop :: Int -> m ()
loop Int
delay = do
Int -> m ()
delayFn Int
delay
Word64
lastTrigger <- forall (m :: * -> *) a. MonadIO m => TVar a -> m a
readTVarIO TVar Word64
timeTVar
Word64
now <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO Word64
getMonotonicTimeNSec
let diff :: Int
diff = forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word64
now forall a. Num a => a -> a -> a
- Word64
lastTrigger) forall a. Integral a => a -> a -> a
`div` Int
1000
shouldWait :: Bool
shouldWait = Int
diff forall a. Ord a => a -> a -> Bool
< Int
freq
if Bool
shouldWait
then
Int -> m ()
loop forall a b. (a -> b) -> a -> b
$ Int
freq forall a. Num a => a -> a -> a
- Int
diff
else do
forall {m :: * -> *}. MonadUnliftIO m => m () -> m ()
ignoreExc m ()
action
Word64
timeAfterAction <- forall (m :: * -> *) a. MonadIO m => TVar a -> m a
readTVarIO TVar Word64
timeTVar
let wasTriggered :: Bool
wasTriggered = Word64
timeAfterAction forall a. Ord a => a -> a -> Bool
> Word64
now
if Bool
wasTriggered
then do
Word64
updatedNow <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO Word64
getMonotonicTimeNSec
let newDiff :: Int
newDiff = forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word64
updatedNow forall a. Num a => a -> a -> a
- Word64
timeAfterAction) forall a. Integral a => a -> a -> a
`div` Int
1000
Int -> m ()
loop forall a b. (a -> b) -> a -> b
$ Int
freq forall a. Num a => a -> a -> a
- Int
newDiff
else
forall (m :: * -> *) a. MonadIO m => MVar a -> a -> m ()
putMVar MVar ()
baton ()
forkAndLabel :: m () -> m ()
forkAndLabel m ()
act = do
ThreadId
tid <- forall (m :: * -> *) a. MonadUnliftIO m => m a -> m a
mask_ forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *). MonadUnliftIO m => m () -> m ThreadId
forkIO m ()
act
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ ThreadId -> String -> IO ()
labelThread ThreadId
tid String
name
ignoreExc :: MonadUnliftIO m => m () -> m ()
ignoreExc :: forall {m :: * -> *}. MonadUnliftIO m => m () -> m ()
ignoreExc = forall (m :: * -> *) e a.
(MonadUnliftIO m, Exception e) =>
(e -> m a) -> m a -> m a
handle forall a b. (a -> b) -> a -> b
$ \(SomeException
_ :: SomeException) -> forall (f :: * -> *) a. Applicative f => a -> f a
pure ()