on master help sign in
Improve auto-update docs
commit346e5e70292ff44653e847dd24a4c01fc0245973 authorMaximilian Tagher <feedback.tagher@gmail.com> dateSat 23 Apr 2016, 12:02 (10 years ago) parent5dc1cc5
2 files changed+61−33
auto-update/Control/AutoUpdate.hs+34−22 auto-update/Control/Debounce.hs+27−11
auto-update/Control/AutoUpdate.hs+34−22
@@ -1,21 +1,33 @@
1−-- | A common problem is the desire to have an action run at a scheduled
2−-- interval, but only if it is needed. For example, instead of having
3−-- every web request result in a new @getCurrentTime@ call, we'd like to
4−-- have a single worker thread run every second, updating an @IORef@.
5−-- However, if the request frequency is less than once per second, this is
6−-- a pessimization, and worse, kills idle GC.
1+-- | In a multithreaded environment, running actions on a regularly scheduled background thread can dramatically improve performance.
2+-- For example, web servers need to return the current time with each HTTP response.
3+-- For a high-volume server, it's much faster for a dedicated thread to run every second, and write the current time to a shared 'IORef', than it is for each request to make its own call to 'getCurrentTime'.
74--
8−-- This library allows you to define actions which will either be
9−-- performed by a dedicated thread or, in times of low volume, will be
10−-- executed by the calling thread.
5+-- But for a low-volume server, whose request frequency is less than once per second, that approach will result in /more/ calls to 'getCurrentTime' than necessary, and worse, kills idle GC.
6+--
7+-- This library solves that problem by allowing you to define actions which will either be performed by a dedicated thread, or, in times of low volume, will be executed by the calling thread.
8+--
9+-- Example usage:
10+--
11+-- @
12+-- import "Data.Time"
13+-- import "Control.AutoUpdate"
14+--
15+-- getTime <- 'mkAutoUpdate' 'defaultUpdateSettings'
16+-- { 'updateAction' = 'Data.Time.Clock.getCurrentTime'
17+-- , 'updateFreq' = 1000000 -- The default frequency, once per second
18+-- }
19+-- currentTime <- getTime
20+-- @
21+--
22+-- For more examples, <http://www.yesodweb.com/blog/2014/08/announcing-auto-update see the blog post introducing this library>.
1123module Control.AutoUpdate (
1224 -- * Type
1325 UpdateSettings
1426 , defaultUpdateSettings
1527 -- * Accessors
28+ , updateAction
1629 , updateFreq
1730 , updateSpawnThreshold
18− , updateAction
1931 -- * Creation
2032 , mkAutoUpdate
2133 ) where
@@ -27,9 +39,9 @@ import Control.Exception (SomeException, catch, throw, mask_, tr
2739import Control.Monad (void)
2840import Data.IORef (newIORef, readIORef, writeIORef)
2941
30−-- | Default value for creating an @UpdateSettings@.
42+-- | Default value for creating an 'UpdateSettings'.
3143--
32−-- Since 0.1.0
44+-- @since 0.1.0
3345defaultUpdateSettings :: UpdateSettings ()
3446defaultUpdateSettings = UpdateSettings
3547 { updateFreq = 1000000
@@ -39,44 +51,44 @@ defaultUpdateSettings = UpdateSettings
3951
4052-- | Settings to control how values are updated.
4153--
42−-- This should be constructed using @defaultUpdateSettings@ and record
54+-- This should be constructed using 'defaultUpdateSettings' and record
4355-- update syntax, e.g.:
4456--
4557-- @
46−-- let set = defaultUpdateSettings { updateAction = getCurrentTime }
58+-- let settings = 'defaultUpdateSettings' { 'updateAction' = 'Data.Time.Clock.getCurrentTime' }
4759-- @
4860--
49−-- Since 0.1.0
61+-- @since 0.1.0
5062data UpdateSettings a = UpdateSettings
5163 { updateFreq :: Int
5264 -- ^ Microseconds between update calls. Same considerations as
53− -- @threadDelay@ apply.
65+ -- 'threadDelay' apply.
5466 --
5567 -- Default: 1 second (1000000)
5668 --
57− -- Since 0.1.0
69+ -- @since 0.1.0
5870 , updateSpawnThreshold :: Int
5971 -- ^ NOTE: This value no longer has any effect, since worker threads are
6072 -- dedicated instead of spawned on demand.
6173 --
62− -- Previously, this determined: How many times the data must be requested
74+ -- Previously, this determined how many times the data must be requested
6375 -- before we decide to spawn a dedicated thread.
6476 --
6577 -- Default: 3
6678 --
67− -- Since 0.1.0
79+ -- @since 0.1.0
6880 , updateAction :: IO a
6981 -- ^ Action to be performed to get the current value.
7082 --
7183 -- Default: does nothing.
7284 --
73− -- Since 0.1.0
85+ -- @since 0.1.0
7486 }
7587
7688-- | Generate an action which will either read from an automatically
7789-- updated value, or run the update action in the current thread.
7890--
79−-- Since 0.1.0
91+-- @since 0.1.0
8092mkAutoUpdate :: UpdateSettings a -> IO (IO a)
8193mkAutoUpdate us = do
8294 -- A baton to tell the worker thread to generate a new value.
@@ -158,7 +170,7 @@ mkAutoUpdate us = do
158170 -- we have a current value, use it
159171 Right val -> return val
160172
161−-- | Turn a runtime exception into an impure exception, so that all @IO@
173+-- | Turn a runtime exception into an impure exception, so that all 'IO'
162174-- actions will complete successfully. This simply defers the exception until
163175-- the value is forced.
164176catchSome :: IO a -> IO a
auto-update/Control/Debounce.hs+27−11
@@ -3,10 +3,26 @@
33-- period of time.
44--
55-- 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.
6+-- flushed to disk at most once per second.
87--
9−-- Since 0.1.2
8+-- Example usage:
9+--
10+-- @
11+-- printString <- 'mkDebounce' 'defaultDebounceSettings'
12+-- { 'debounceAction' = putStrLn "Running action"
13+-- , 'debounceFreq' = 5000000 -- 5 seconds
14+-- }
15+-- @
16+--
17+-- >>> printString
18+-- Running action
19+-- >>> printString
20+-- <Wait five seconds>
21+-- Running action
22+--
23+-- See the fast-logger package ("System.Log.FastLogger") for real-world usage.
24+--
25+-- @since 0.1.2
1026module Control.Debounce
1127 ( -- * Type
1228 DebounceSettings
@@ -25,14 +41,14 @@ import Control.Monad (forever, void)
2541
2642-- | Settings to control how debouncing should work.
2743--
28−-- This should be constructed using @defaultDebounceSettings@ and record
44+-- This should be constructed using 'defaultDebounceSettings' and record
2945-- update syntax, e.g.:
3046--
3147-- @
32−-- let set = defaultDebounceSettings { debounceAction = flushLog }
48+-- let settings = 'defaultDebounceSettings' { 'debounceAction' = flushLog }
3349-- @
3450--
35−-- Since 0.1.2
51+-- @since 0.1.2
3652data DebounceSettings = DebounceSettings
3753 { debounceFreq :: Int
3854 -- ^ Microseconds lag required between subsequence calls to the debounced
@@ -40,7 +56,7 @@ data DebounceSettings = DebounceSettings
4056 --
4157 -- Default: 1 second (1000000)
4258 --
43− -- Since 0.1.2
59+ -- @since 0.1.2
4460 , debounceAction :: IO ()
4561 -- ^ Action to be performed.
4662 --
@@ -48,12 +64,12 @@ data DebounceSettings = DebounceSettings
4864 --
4965 -- Default: does nothing.
5066 --
51− -- Since 0.1.2
67+ -- @since 0.1.2
5268 }
5369
54−-- | Default value for creating a @DebounceSettings@.
70+-- | Default value for creating a 'DebounceSettings'.
5571--
56−-- Since 0.1.2
72+-- @since 0.1.2
5773defaultDebounceSettings :: DebounceSettings
5874defaultDebounceSettings = DebounceSettings
5975 { debounceFreq = 1000000
@@ -64,7 +80,7 @@ defaultDebounceSettings = DebounceSettings
6480-- performed. The action will either be performed immediately, or after the
6581-- current cooldown period has expired.
6682--
67−-- Since 0.1.2
83+-- @since 0.1.2
6884mkDebounce :: DebounceSettings -> IO (IO ())
6985mkDebounce (DebounceSettings freq action) = do
7086 baton <- newEmptyMVar