@@ -1,21 +1,33 @@
1−-- | A common problem is the desire to have an action run at a scheduled
1+-- | In a multithreaded environment, running actions on a regularly scheduled background thread can dramatically improve performance.
2−-- interval, but only if it is needed. For example, instead of having
2+-- For example, web servers need to return the current time with each HTTP response.
3−-- every web request result in a new @getCurrentTime@ call, we'd like to
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'.
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.
8−-- This library allows you to define actions which will either be
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.
9−-- performed by a dedicated thread or, in times of low volume, will be
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.
10−-- executed by the calling thread.
8+--
13+-- import "Control.AutoUpdate"
15+-- getTime <- 'mkAutoUpdate' 'defaultUpdateSettings'
16+-- { 'updateAction' = 'Data.Time.Clock.getCurrentTime'
17+-- , 'updateFreq' = 1000000 -- The default frequency, once per second
19+-- currentTime <- getTime
22+-- For more examples, <http://www.yesodweb.com/blog/2014/08/announcing-auto-update see the blog post introducing this library>.
11module Control.AutoUpdate (
23module Control.AutoUpdate (
13 UpdateSettings
25 UpdateSettings
14 , defaultUpdateSettings
26 , defaultUpdateSettings
15 -- * Accessors
27 -- * Accessors
16 , updateFreq
29 , updateFreq
17 , updateSpawnThreshold
30 , updateSpawnThreshold
19 -- * Creation
31 -- * Creation
20 , mkAutoUpdate
32 , mkAutoUpdate
@@ -27,9 +39,9 @@ import Control.Exception (SomeException, catch, throw, mask_, tr
27import Control.Monad (void)
39import Control.Monad (void)
28import Data.IORef (newIORef, readIORef, writeIORef)
40import Data.IORef (newIORef, readIORef, writeIORef)
30−-- | Default value for creating an @UpdateSettings@.
42+-- | Default value for creating an 'UpdateSettings'.
32−-- Since 0.1.0
44+-- @since 0.1.0
33defaultUpdateSettings :: UpdateSettings ()
45defaultUpdateSettings :: UpdateSettings ()
34defaultUpdateSettings = UpdateSettings
46defaultUpdateSettings = UpdateSettings
35 { updateFreq = 1000000
47 { updateFreq = 1000000
@@ -39,44 +51,44 @@ defaultUpdateSettings = UpdateSettings
40-- | Settings to control how values are updated.
52-- | Settings to control how values are updated.
42−-- This should be constructed using @defaultUpdateSettings@ and record
54+-- This should be constructed using 'defaultUpdateSettings' and record
43-- update syntax, e.g.:
55-- update syntax, e.g.:
46−-- let set = defaultUpdateSettings { updateAction = getCurrentTime }
58+-- let settings = 'defaultUpdateSettings' { 'updateAction' = 'Data.Time.Clock.getCurrentTime' }
49−-- Since 0.1.0
61+-- @since 0.1.0
50data UpdateSettings a = UpdateSettings
62data UpdateSettings a = UpdateSettings
51 { updateFreq :: Int
63 { updateFreq :: Int
52 -- ^ Microseconds between update calls. Same considerations as
64 -- ^ Microseconds between update calls. Same considerations as
53− -- @threadDelay@ apply.
65+ -- 'threadDelay' apply.
55 -- Default: 1 second (1000000)
67 -- Default: 1 second (1000000)
57− -- Since 0.1.0
69+ -- @since 0.1.0
58 , updateSpawnThreshold :: Int
70 , updateSpawnThreshold :: Int
59 -- ^ NOTE: This value no longer has any effect, since worker threads are
71 -- ^ NOTE: This value no longer has any effect, since worker threads are
60 -- dedicated instead of spawned on demand.
72 -- dedicated instead of spawned on demand.
62− -- Previously, this determined: How many times the data must be requested
74+ -- Previously, this determined how many times the data must be requested
63 -- before we decide to spawn a dedicated thread.
75 -- before we decide to spawn a dedicated thread.
65 -- Default: 3
77 -- Default: 3
67− -- Since 0.1.0
79+ -- @since 0.1.0
68 , updateAction :: IO a
80 , updateAction :: IO a
69 -- ^ Action to be performed to get the current value.
81 -- ^ Action to be performed to get the current value.
71 -- Default: does nothing.
83 -- Default: does nothing.
73− -- Since 0.1.0
85+ -- @since 0.1.0
76-- | Generate an action which will either read from an automatically
88-- | Generate an action which will either read from an automatically
77-- updated value, or run the update action in the current thread.
89-- updated value, or run the update action in the current thread.
79−-- Since 0.1.0
91+-- @since 0.1.0
80mkAutoUpdate :: UpdateSettings a -> IO (IO a)
92mkAutoUpdate :: UpdateSettings a -> IO (IO a)
81mkAutoUpdate us = do
93mkAutoUpdate us = do
82 -- A baton to tell the worker thread to generate a new value.
94 -- A baton to tell the worker thread to generate a new value.
@@ -158,7 +170,7 @@ mkAutoUpdate us = do
158 -- we have a current value, use it
170 -- we have a current value, use it
159 Right val -> return val
171 Right val -> return val
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'
162-- actions will complete successfully. This simply defers the exception until
174-- actions will complete successfully. This simply defers the exception until
163-- the value is forced.
175-- the value is forced.
164catchSome :: IO a -> IO a
176catchSome :: IO a -> IO a