on master help sign in
Add Control.Debounce kazu-yamamoto/logger#45
commit7880a55bef8f22a6e5fb6572ca9d98aa15011617 authorMichael Snoyman <michael@snoyman.com> dateThu 16 Oct 2014, 19:53 (12 years ago) parent35c2751
3 files changed+82−1
auto-update/Control/Debounce.hs+78 auto-update/auto-update.cabal+3−1 auto-update/changelog.md+1
auto-update/Control/Debounce.hsnew file+78
@@ -0,0 +1,78 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
2+-- | Debounce an action, ensuring it doesn't occur more than once for a given
3+-- period of time.
4+--
5+-- This is useful as an optimization, for example to ensure that logs are only
6+-- flushed to disk at most once per second. See the fast-logger package for an
7+-- example usage.
8+--
9+-- Since 0.1.2
10+module Control.Debounce
11+ ( -- * Type
12+ DebounceSettings
13+ , defaultDebounceSettings
14+ -- * Accessors
15+ , debounceFreq
16+ , debounceAction
17+ -- * Creation
18+ , mkDebounce
19+ ) where
20+
21+import Control.Concurrent (forkIO, threadDelay)
22+import Control.Concurrent.MVar (newEmptyMVar, takeMVar, tryPutMVar)
23+import Control.Exception (SomeException, handle, mask_)
24+import Control.Monad (forever, void)
25+
26+-- | Settings to control how debouncing should work.
27+--
28+-- This should be constructed using @defaultDebounceSettings@ and record
29+-- update syntax, e.g.:
30+--
31+-- @
32+-- let set = defaultDebounceSettings { debounceAction = flushLog }
33+-- @
34+--
35+-- Since 0.1.2
36+data DebounceSettings = DebounceSettings
37+ { debounceFreq :: Int
38+ -- ^ Microseconds lag required between subsequence calls to the debounced
39+ -- action.
40+ --
41+ -- Default: 1 second (1000000)
42+ --
43+ -- Since 0.1.2
44+ , debounceAction :: IO ()
45+ -- ^ Action to be performed.
46+ --
47+ -- Note: all exceptions thrown by this action will be silently discarded.
48+ --
49+ -- Default: does nothing.
50+ --
51+ -- Since 0.1.2
52+ }
53+
54+-- | Default value for creating a @DebounceSettings@.
55+--
56+-- Since 0.1.2
57+defaultDebounceSettings :: DebounceSettings
58+defaultDebounceSettings = DebounceSettings
59+ { debounceFreq = 1000000
60+ , debounceAction = return ()
61+ }
62+
63+-- | Generate an action which will trigger the debounced action to be
64+-- performed. The action will either be performed immediately, or after the
65+-- current cooldown period has expired.
66+--
67+-- Since 0.1.2
68+mkDebounce :: DebounceSettings -> IO (IO ())
69+mkDebounce (DebounceSettings freq action) = do
70+ baton <- newEmptyMVar
71+ mask_ $ void $ forkIO $ forever $ do
72+ takeMVar baton
73+ ignoreExc action
74+ threadDelay freq
75+ return $ void $ tryPutMVar baton ()
76+
77+ignoreExc :: IO () -> IO ()
78+ignoreExc = handle $ \(_ :: SomeException) -> return ()
auto-update/auto-update.cabal+3−1
@@ -1,5 +1,5 @@
11name: auto-update
2−version: 0.1.1.5
2+version: 0.1.2
33synopsis: Efficiently run periodic, on-demand actions
44description:
55 A common problem is the desire to have an action run at a scheduled interval, but only if it is needed. For example, instead of having every web request result in a new @getCurrentTime@ call, we'd like to have a single worker thread run every second, updating an @IORef@. However, if the request frequency is less than once per second, this is a pessimization, and worse, kills idle GC.
@@ -13,11 +13,13 @@ maintainer: michael@snoyman.com
1313category: Control
1414build-type: Simple
1515extra-source-files: README.md
16+ changelog.md
1617cabal-version: >=1.10
1718
1819library
1920 ghc-options: -Wall
2021 exposed-modules: Control.AutoUpdate
22+ Control.Debounce
2123 Control.Reaper
2224 other-modules: Control.AutoUpdate.Util
2325 build-depends: base >= 4 && < 5
auto-update/changelog.mdnew file+1
@@ -0,0 +1 @@
1+__0.1.2__ Added Control.Debounce