diff --git a/Control/AutoUpdate.hs b/Control/AutoUpdate.hs
new file mode 100644
--- /dev/null
+++ b/Control/AutoUpdate.hs
@@ -0,0 +1,184 @@
+{-# LANGUAGE BangPatterns       #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE CPP #-}
+
+-- | 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.
+--
+-- This library allows 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.
+module Control.AutoUpdate
+    ( UpdateSettings
+    , defaultUpdateSettings
+    , updateFreq
+    , updateSpawnThreshold
+    , updateAction
+    , mkAutoUpdate
+    ) where
+
+import           Control.Concurrent (ThreadId, forkIO, myThreadId, threadDelay)
+import           Control.Exception  (Exception, SomeException
+                                    ,assert, fromException, handle,throwIO, throwTo)
+import           Control.Monad      (forever, join)
+import           Data.IORef         (IORef, newIORef)
+import           Data.Typeable      (Typeable)
+
+#ifndef MIN_VERSION_base
+#define MIN_VERSION_base(x,y,z) 1
+#endif
+
+#if MIN_VERSION_base(4,6,0)
+import           Data.IORef         (atomicModifyIORef')
+#else
+import           Data.IORef         (atomicModifyIORef)
+-- | Strict version of 'atomicModifyIORef'.  This forces both the value stored
+-- in the 'IORef' as well as the value returned.
+atomicModifyIORef' :: IORef a -> (a -> (a,b)) -> IO b
+atomicModifyIORef' ref f = do
+    c <- atomicModifyIORef ref
+            (\x -> let (a, b) = f x    -- Lazy application of "f"
+                    in (a, a `seq` b)) -- Lazy application of "seq"
+    -- The following forces "a `seq` b", so it also forces "f x".
+    c `seq` return c
+#endif
+
+-- | Default value for creating an @UpdateSettings@.
+--
+-- Since 0.1.0
+defaultUpdateSettings :: UpdateSettings ()
+defaultUpdateSettings = UpdateSettings
+    { updateFreq = 1000000
+    , updateSpawnThreshold = 3
+    , updateAction = return ()
+    }
+
+-- | Settings to control how values are updated.
+--
+-- This should be constructed using @defaultUpdateSettings@ and record
+-- update syntax, e.g.:
+--
+-- @
+-- let set = defaultUpdateSettings { updateAction = getCurrentTime }
+-- @
+--
+-- Since 0.1.0
+data UpdateSettings a = UpdateSettings
+    { updateFreq           :: Int
+    -- ^ Microseconds between update calls. Same considerations as
+    -- @threadDelay@ apply.
+    --
+    -- Default: 1 second (1000000)
+    --
+    -- Since 0.1.0
+    , updateSpawnThreshold :: Int
+    -- ^ How many times the data must be requested before we decide to
+    -- spawn a dedicated thread.
+    --
+    -- Default: 3
+    --
+    -- Since 0.1.0
+    , updateAction         :: IO a
+    -- ^ Action to be performed to get the current value.
+    --
+    -- Default: does nothing.
+    --
+    -- Since 0.1.0
+    }
+
+data Status a = AutoUpdated
+                    !a
+                    {-# UNPACK #-} !Int
+                    -- Number of times used since last updated.
+                    {-# UNPACK #-} !ThreadId
+                    -- Worker thread.
+              | ManualUpdates
+                    {-# UNPACK #-} !Int
+                    -- Number of times used since we started/switched
+                    -- off manual updates.
+
+-- | Generate an action which will either read from an automatically
+-- updated value, or run the update action in the current thread.
+--
+-- Since 0.1.0
+mkAutoUpdate :: UpdateSettings a -> IO (IO a)
+mkAutoUpdate (UpdateSettings !f !t !a) = do
+    istatus <- newIORef $ ManualUpdates 0
+    return $! getCurrent f t a istatus
+
+data Action a = Return a | Manual | Spawn
+
+data Replaced = Replaced deriving (Show, Typeable)
+instance Exception Replaced
+
+-- | Get the current value, either fed from an auto-update thread, or
+-- computed manually in the current thread.
+--
+-- Since 0.1.0
+getCurrent :: Int -- ^ frequency
+           -> Int -- ^ spawn threshold
+           -> IO a -- ^ internal update action
+           -> IORef (Status a) -- ^ mutable state
+           -> IO a
+getCurrent freq spawnThreshold update istatus = do
+    ea <- atomicModifyIORef' istatus increment
+    case ea of
+        Return a -> return a
+        Manual   -> update
+        Spawn    -> do
+            a <- update
+            tid <- forkIO $ spawn freq update istatus
+            join $ atomicModifyIORef' istatus $ turnToAuto a tid
+            return a
+  where
+    increment (AutoUpdated a cnt tid) = (AutoUpdated a (succ cnt) tid, Return a)
+    increment (ManualUpdates i)       = (ManualUpdates (succ i),       act)
+      where
+        act = if i > spawnThreshold then Spawn else Manual
+
+    -- Normal case.
+    turnToAuto a tid (ManualUpdates cnt)     = (AutoUpdated a cnt tid
+                                               ,return ())
+    -- Race condition: multiple threads were spawned.
+    -- So, let's kill the previous one by this thread.
+    turnToAuto a tid (AutoUpdated _ cnt old) = (AutoUpdated a cnt tid
+                                               ,throwTo old Replaced)
+
+spawn :: Int -> IO a -> IORef (Status a) -> IO ()
+spawn freq update istatus = handle (onErr istatus) $ forever $ do
+    threadDelay freq
+    a <- update
+    join $ atomicModifyIORef' istatus $ turnToManual a
+  where
+    -- Normal case.
+    turnToManual a (AutoUpdated _ cnt tid)
+      | cnt >= 1                     = (AutoUpdated a 0 tid, return ())
+      | otherwise                    = (ManualUpdates 0, stop)
+    -- This case must not happen.
+    turnToManual _ (ManualUpdates i) =  assert False (ManualUpdates i, stop)
+
+onErr :: IORef (Status a) -> SomeException -> IO ()
+onErr istatus ex = case fromException ex of
+    Just Replaced -> return ()
+    Nothing -> do
+        tid <- myThreadId
+        atomicModifyIORef' istatus $ clear tid
+        throwIO ex
+  where
+    -- In the race condition described above,
+    -- suppose thread A is running, and is killed by thread B.
+    -- Thread B then updates the IORef to refer to thread B.
+    -- Then thread A's exception handler fires.
+    -- We don't want to modify the IORef at all,
+    -- since it refers to thread B already.
+    -- Solution: only switch back to manual updates
+    -- if the IORef is pointing at the current thread.
+    clear tid (AutoUpdated _ _ tid') | tid == tid' = (ManualUpdates 0, ())
+    clear _   status                               = (status, ())
+
+stop :: IO a
+stop = throwIO Replaced
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2014 Michael Snoyman
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+See https://github.com/yesodweb/yesod-scaffold/pull/15
+
+Todo:
+
+- Add ThreadId to prevent race condition.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/auto-update.cabal b/auto-update.cabal
new file mode 100644
--- /dev/null
+++ b/auto-update.cabal
@@ -0,0 +1,29 @@
+name:                auto-update
+version:             0.1.0.0
+synopsis:            Efficiently run periodic, on-demand actions
+description:
+    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.
+    .
+    This library allows 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.
+homepage:            https://github.com/yesodweb/wai
+license:             MIT
+license-file:        LICENSE
+author:              Michael Snoyman
+maintainer:          michael@snoyman.com
+category:            Control
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Control.AutoUpdate
+  build-depends:       base >= 4 && < 5
+  default-language:    Haskell2010
+
+test-suite spec
+  main-is:         Spec.hs
+  other-modules:   Control.AutoUpdateSpec
+  hs-source-dirs:  test
+  type:            exitcode-stdio-1.0
+  build-depends:   base, auto-update, hspec
+  default-language:    Haskell2010
diff --git a/test/Control/AutoUpdateSpec.hs b/test/Control/AutoUpdateSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Control/AutoUpdateSpec.hs
@@ -0,0 +1,34 @@
+module Control.AutoUpdateSpec (spec) where
+
+import Test.Hspec
+import Test.Hspec.QuickCheck
+import Data.IORef
+import Control.Concurrent (threadDelay)
+import Control.Monad (replicateM_, forM_)
+import Control.AutoUpdate
+
+spec :: Spec
+spec = do
+    prop "incrementer" $ \st' -> do
+        let st = abs st'
+        ref <- newIORef 0
+        next <- mkAutoUpdate defaultUpdateSettings
+            { updateAction = atomicModifyIORef ref $ \i ->
+                let i' = succ i in i' `seq` (i', i')
+            , updateSpawnThreshold = st
+            , updateFreq = 10000
+            }
+
+        forM_ [1..st + 1] $ \i -> do
+            j <- next
+            j `shouldBe` i
+
+        replicateM_ 50 $ do
+            i <- next
+            i `shouldBe` st + 2
+
+        threadDelay 60000
+        last1 <- readIORef ref
+        threadDelay 20000
+        last2 <- readIORef ref
+        last2 `shouldBe` last1
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
