Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package ghc-auto-update for openSUSE:Factory
checked in at 2024-11-12 19:20:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/ghc-auto-update (Old)
and /work/SRC/openSUSE:Factory/.ghc-auto-update.new.2017 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ghc-auto-update"
Tue Nov 12 19:20:12 2024 rev:19 rq:1222953 version:0.2.2
Changes:
--------
--- /work/SRC/openSUSE:Factory/ghc-auto-update/ghc-auto-update.changes
2023-04-04 21:18:35.040487905 +0200
+++
/work/SRC/openSUSE:Factory/.ghc-auto-update.new.2017/ghc-auto-update.changes
2024-11-12 19:20:37.345459271 +0100
@@ -1,0 +2,22 @@
+Tue Oct 29 05:50:49 UTC 2024 - Peter Simons <[email protected]>
+
+- Update auto-update to version 0.2.2.
+ ## 0.2.2
+
+ * NewAPI: updateThreadName, reaperThreadName, debounceThreadName:
+ Names can be given via this field to threads
+ for GHC.Conc.Sync.listThreads.
+
+ ## 0.2.1
+
+ * Labeling threads.
+
+ ## 0.2.0
+
+ * Creating Reaper.Internal to export Reaper constructor.
+ * Hiding Reaper constructor.
+ * Add `reaperModify` to the `Reaper` API, allowing workload modification
outside
+ of the main `reaperAction` loop.
+ [#985](https://github.com/yesodweb/wai/pull/985)
+
+-------------------------------------------------------------------
Old:
----
auto-update-0.1.6.tar.gz
New:
----
auto-update-0.2.2.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ ghc-auto-update.spec ++++++
--- /var/tmp/diff_new_pack.pquns2/_old 2024-11-12 19:20:38.309499662 +0100
+++ /var/tmp/diff_new_pack.pquns2/_new 2024-11-12 19:20:38.309499662 +0100
@@ -1,7 +1,7 @@
#
# spec file for package ghc-auto-update
#
-# Copyright (c) 2023 SUSE LLC
+# Copyright (c) 2024 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -20,7 +20,7 @@
%global pkgver %{pkg_name}-%{version}
%bcond_with tests
Name: ghc-%{pkg_name}
-Version: 0.1.6
+Version: 0.2.2
Release: 0
Summary: Efficiently run periodic, on-demand actions
License: MIT
++++++ auto-update-0.1.6.tar.gz -> auto-update-0.2.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/auto-update-0.1.6/ChangeLog.md
new/auto-update-0.2.2/ChangeLog.md
--- old/auto-update-0.1.6/ChangeLog.md 2019-07-09 09:40:38.000000000 +0200
+++ new/auto-update-0.2.2/ChangeLog.md 2001-09-09 03:46:40.000000000 +0200
@@ -1,5 +1,23 @@
# ChangeLog for auto-update
+## 0.2.2
+
+* NewAPI: updateThreadName, reaperThreadName, debounceThreadName:
+ Names can be given via this field to threads
+ for GHC.Conc.Sync.listThreads.
+
+## 0.2.1
+
+* Labeling threads.
+
+## 0.2.0
+
+* Creating Reaper.Internal to export Reaper constructor.
+* Hiding Reaper constructor.
+* Add `reaperModify` to the `Reaper` API, allowing workload modification
outside
+ of the main `reaperAction` loop.
+ [#985](https://github.com/yesodweb/wai/pull/985)
+
## 0.1.6
* Add control of activation on leading vs. trailing edges for Control.Debounce
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/auto-update-0.1.6/Control/AutoUpdate/Util.hs
new/auto-update-0.2.2/Control/AutoUpdate/Util.hs
--- old/auto-update-0.1.6/Control/AutoUpdate/Util.hs 2015-11-09
03:12:10.000000000 +0100
+++ new/auto-update-0.2.2/Control/AutoUpdate/Util.hs 2001-09-09
03:46:40.000000000 +0200
@@ -1,7 +1,8 @@
{-# LANGUAGE CPP #-}
-module Control.AutoUpdate.Util
- ( atomicModifyIORef'
- ) where
+
+module Control.AutoUpdate.Util (
+ atomicModifyIORef',
+) where
#ifndef MIN_VERSION_base
#define MIN_VERSION_base(x,y,z) 1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/auto-update-0.1.6/Control/AutoUpdate.hs
new/auto-update-0.2.2/Control/AutoUpdate.hs
--- old/auto-update-0.1.6/Control/AutoUpdate.hs 2017-06-01 14:48:32.000000000
+0200
+++ new/auto-update-0.2.2/Control/AutoUpdate.hs 2001-09-09 03:46:40.000000000
+0200
@@ -1,4 +1,5 @@
{-# LANGUAGE CPP #-}
+
-- | In a multithreaded environment, running actions on a regularly scheduled
-- background thread can dramatically improve performance.
-- For example, web servers need to return the current time with each HTTP
response.
@@ -6,12 +7,12 @@
-- second, and write the current time to a shared 'IORef', than it is for each
-- request to make its own call to 'getCurrentTime'.
--
--- 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
+-- 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.
--
-- 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
+-- either be performed by a dedicated thread, or, in times of low volume, will
-- be executed by the calling thread.
--
-- Example usage:
@@ -29,38 +30,55 @@
--
-- For more examples,
<http://www.yesodweb.com/blog/2014/08/announcing-auto-update see the blog post
introducing this library>.
module Control.AutoUpdate (
- -- * Type
- UpdateSettings
- , defaultUpdateSettings
- -- * Accessors
- , updateAction
- , updateFreq
- , updateSpawnThreshold
- -- * Creation
- , mkAutoUpdate
- , mkAutoUpdateWithModify
- ) where
+ -- * Type
+ UpdateSettings,
+ defaultUpdateSettings,
+
+ -- * Accessors
+ updateAction,
+ updateFreq,
+ updateSpawnThreshold,
+ updateThreadName,
+
+ -- * Creation
+ mkAutoUpdate,
+ mkAutoUpdateWithModify,
+) where
#if __GLASGOW_HASKELL__ < 709
import Control.Applicative ((<*>))
#endif
-import Control.Concurrent (forkIO, threadDelay)
-import Control.Concurrent.MVar (newEmptyMVar, putMVar, readMVar,
- takeMVar, tryPutMVar)
-import Control.Exception (SomeException, catch, mask_, throw,
- try)
-import Control.Monad (void)
-import Data.IORef (newIORef, readIORef, writeIORef)
+import Control.Concurrent (forkIO, threadDelay)
+import Control.Concurrent.MVar (
+ newEmptyMVar,
+ putMVar,
+ readMVar,
+ takeMVar,
+ tryPutMVar,
+ )
+import Control.Exception (
+ SomeException,
+ catch,
+ mask_,
+ throw,
+ try,
+ )
+import Control.Monad (void)
+import Data.IORef (newIORef, readIORef, writeIORef)
+import Data.Maybe (fromMaybe)
+import GHC.Conc.Sync (labelThread)
-- | Default value for creating an 'UpdateSettings'.
--
-- @since 0.1.0
defaultUpdateSettings :: UpdateSettings ()
-defaultUpdateSettings = UpdateSettings
- { updateFreq = 1000000
- , updateSpawnThreshold = 3
- , updateAction = return ()
- }
+defaultUpdateSettings =
+ UpdateSettings
+ { updateFreq = 1000000
+ , updateSpawnThreshold = 3
+ , updateAction = return ()
+ , updateThreadName = "AutoUpdate"
+ }
-- | Settings to control how values are updated.
--
@@ -73,7 +91,7 @@
--
-- @since 0.1.0
data UpdateSettings a = UpdateSettings
- { updateFreq :: Int
+ { updateFreq :: Int
-- ^ Microseconds between update calls. Same considerations as
-- 'threadDelay' apply.
--
@@ -90,12 +108,13 @@
-- Default: 3
--
-- @since 0.1.0
- , updateAction :: IO a
+ , updateAction :: IO a
-- ^ Action to be performed to get the current value.
--
-- Default: does nothing.
--
-- @since 0.1.0
+ , updateThreadName :: String
}
-- | Generate an action which will either read from an automatically
@@ -136,12 +155,16 @@
let fillRefOnExit f = do
eres <- try f
case eres of
- Left e -> writeIORef currRef $ error $
- "Control.AutoUpdate.mkAutoUpdate: worker thread exited
with exception: "
- ++ show (e :: SomeException)
- Right () -> writeIORef currRef $ error $
- "Control.AutoUpdate.mkAutoUpdate: worker thread exited
normally, "
- ++ "which should be impossible due to usage of infinite
loop"
+ Left e ->
+ writeIORef currRef $
+ error $
+ "Control.AutoUpdate.mkAutoUpdate: worker thread
exited with exception: "
+ ++ show (e :: SomeException)
+ Right () ->
+ writeIORef currRef $
+ error $
+ "Control.AutoUpdate.mkAutoUpdate: worker thread
exited normally, "
+ ++ "which should be impossible due to usage of
infinite loop"
-- fork the worker thread immediately. Note that we mask async exceptions,
-- but *not* in an uninterruptible manner. This will allow a
@@ -153,7 +176,7 @@
-- Note that since we throw away the ThreadId of this new thread and never
-- calls myThreadId, normal async exceptions can never be thrown to it,
-- only RTS exceptions.
- mask_ $ void $ forkIO $ fillRefOnExit $ do
+ tid <- mask_ $ forkIO $ fillRefOnExit $ do
-- This infinite loop makes up out worker thread. It takes an a
-- responseVar value where the next value should be putMVar'ed to for
-- the benefit of any requesters currently blocked on it.
@@ -162,7 +185,7 @@
takeMVar needsRunning
-- new value requested, so run the updateAction
- a <- catchSome $ maybe (updateAction us) id
(updateActionModify <*> maybea)
+ a <- catchSome $ fromMaybe (updateAction us)
(updateActionModify <*> maybea)
-- we got a new value, update currRef and lastValue
writeIORef currRef $ Right a
@@ -181,7 +204,7 @@
-- Kick off the loop, with the initial responseVar0 variable.
loop responseVar0 Nothing
-
+ labelThread tid $ updateThreadName us
return $ do
mval <- readIORef currRef
case mval of
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/auto-update-0.1.6/Control/Debounce/Internal.hs
new/auto-update-0.2.2/Control/Debounce/Internal.hs
--- old/auto-update-0.1.6/Control/Debounce/Internal.hs 2019-07-09
09:40:38.000000000 +0200
+++ new/auto-update-0.2.2/Control/Debounce/Internal.hs 2001-09-09
03:46:40.000000000 +0200
@@ -2,17 +2,23 @@
-- | Unstable API which exposes internals for testing.
module Control.Debounce.Internal (
- DebounceSettings(..)
- , DebounceEdge(..)
- , leadingEdge
- , trailingEdge
- , mkDebounceInternal
- ) where
-
-import Control.Concurrent (forkIO)
-import Control.Concurrent.MVar (takeMVar, tryPutMVar, tryTakeMVar,
MVar)
-import Control.Exception (SomeException, handle, mask_)
-import Control.Monad (forever, void)
+ DebounceSettings (..),
+ DebounceEdge (..),
+ leadingEdge,
+ trailingEdge,
+ mkDebounceInternal,
+) where
+
+import Control.Concurrent (forkIO)
+import Control.Concurrent.MVar (
+ MVar,
+ takeMVar,
+ tryPutMVar,
+ tryTakeMVar,
+ )
+import Control.Exception (SomeException, handle, mask_)
+import Control.Monad (forever, void)
+import GHC.Conc.Sync (labelThread)
-- | Settings to control how debouncing should work.
--
@@ -25,7 +31,7 @@
--
-- @since 0.1.2
data DebounceSettings = DebounceSettings
- { debounceFreq :: Int
+ { debounceFreq :: Int
-- ^ Length of the debounce timeout period in microseconds.
--
-- Default: 1 second (1000000)
@@ -46,22 +52,22 @@
-- Default: 'trailingEdge'.
--
-- @since 0.1.6
+ , debounceThreadName :: String
}
-- | Setting to control whether the action happens at the leading and/or
trailing
-- edge of the timeout.
--
-- @since 0.1.6
-data DebounceEdge =
- Leading
- -- ^ Perform the action immediately, and then begin a cooldown period.
- -- If the trigger happens again during the cooldown, wait until the end of
the cooldown
- -- and then perform the action again, then enter a new cooldown period.
- | Trailing
- -- ^ Start a cooldown period and perform the action when the period ends. If
another trigger
- -- happens during the cooldown, it has no effect.
- deriving (Show, Eq)
-
+data DebounceEdge
+ = -- | Perform the action immediately, and then begin a cooldown period.
+ -- If the trigger happens again during the cooldown, wait until the end
of the cooldown
+ -- and then perform the action again, then enter a new cooldown period.
+ Leading
+ | -- | Start a cooldown period and perform the action when the period
ends. If another trigger
+ -- happens during the cooldown, it has no effect.
+ Trailing
+ deriving (Show, Eq)
-- | Perform the action immediately, and then begin a cooldown period.
-- If the trigger happens again during the cooldown, wait until the end of the
cooldown
@@ -78,20 +84,21 @@
trailingEdge :: DebounceEdge
trailingEdge = Trailing
-mkDebounceInternal :: MVar () -> (Int -> IO ()) -> DebounceSettings -> IO (IO
())
-mkDebounceInternal baton delayFn (DebounceSettings freq action edge) = do
- mask_ $ void $ forkIO $ forever $ do
+mkDebounceInternal
+ :: MVar () -> (Int -> IO ()) -> DebounceSettings -> IO (IO ())
+mkDebounceInternal baton delayFn (DebounceSettings freq action edge name) = do
+ tid <- mask_ $ forkIO $ forever $ do
takeMVar baton
case edge of
- Leading -> do
- ignoreExc action
- delayFn freq
- Trailing -> do
- delayFn freq
- -- Empty the baton of any other activations during the interval
- void $ tryTakeMVar baton
- ignoreExc action
-
+ Leading -> do
+ ignoreExc action
+ delayFn freq
+ Trailing -> do
+ delayFn freq
+ -- Empty the baton of any other activations during the interval
+ void $ tryTakeMVar baton
+ ignoreExc action
+ labelThread tid name
return $ void $ tryPutMVar baton ()
ignoreExc :: IO () -> IO ()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/auto-update-0.1.6/Control/Debounce.hs
new/auto-update-0.2.2/Control/Debounce.hs
--- old/auto-update-0.1.6/Control/Debounce.hs 2019-07-09 09:40:38.000000000
+0200
+++ new/auto-update-0.2.2/Control/Debounce.hs 2001-09-09 03:46:40.000000000
+0200
@@ -1,4 +1,3 @@
-{-# LANGUAGE ScopedTypeVariables #-}
-- | Debounce an action, ensuring it doesn't occur more than once for a given
-- period of time.
--
@@ -8,53 +7,57 @@
-- Example usage:
--
-- @
--- printString <- 'mkDebounce' 'defaultDebounceSettings'
+-- > printString <- 'mkDebounce' 'defaultDebounceSettings'
-- { 'debounceAction' = putStrLn "Running action"
-- , 'debounceFreq' = 5000000 -- 5 seconds
-- , 'debounceEdge' = 'DI.trailingEdge' -- Trigger on the
trailing edge
-- }
--- @
---
--- >>> printString
+-- > printString
-- Running action
--- >>> printString
--- <Wait five seconds>
+-- > printString
+-- \<Wait five seconds>
-- Running action
+-- @
--
-- See the fast-logger package ("System.Log.FastLogger") for real-world usage.
--
-- @since 0.1.2
-module Control.Debounce
- ( -- * Type
- DI.DebounceSettings
- , defaultDebounceSettings
- -- * Accessors
- , DI.debounceFreq
- , DI.debounceAction
- , DI.debounceEdge
- , DI.leadingEdge
- , DI.trailingEdge
- -- * Creation
- , mkDebounce
- ) where
+module Control.Debounce (
+ -- * Type
+ DI.DebounceSettings,
+ defaultDebounceSettings,
+
+ -- * Accessors
+ DI.debounceFreq,
+ DI.debounceAction,
+ DI.debounceEdge,
+ DI.debounceThreadName,
+ DI.leadingEdge,
+ DI.trailingEdge,
+
+ -- * Creation
+ mkDebounce,
+) where
-import Control.Concurrent (newEmptyMVar, threadDelay)
+import Control.Concurrent (newEmptyMVar, threadDelay)
import qualified Control.Debounce.Internal as DI
-- | Default value for creating a 'DebounceSettings'.
--
-- @since 0.1.2
defaultDebounceSettings :: DI.DebounceSettings
-defaultDebounceSettings = DI.DebounceSettings
- { DI.debounceFreq = 1000000
- , DI.debounceAction = return ()
- , DI.debounceEdge = DI.leadingEdge
- }
+defaultDebounceSettings =
+ DI.DebounceSettings
+ { DI.debounceFreq = 1000000
+ , DI.debounceAction = return ()
+ , DI.debounceEdge = DI.leadingEdge
+ , DI.debounceThreadName = "Debounce"
+ }
-- | Generate an action which will trigger the debounced action to be
performed.
--
-- @since 0.1.2
mkDebounce :: DI.DebounceSettings -> IO (IO ())
mkDebounce settings = do
- baton <- newEmptyMVar
- DI.mkDebounceInternal baton threadDelay settings
+ baton <- newEmptyMVar
+ DI.mkDebounceInternal baton threadDelay settings
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/auto-update-0.1.6/Control/Reaper/Internal.hs
new/auto-update-0.2.2/Control/Reaper/Internal.hs
--- old/auto-update-0.1.6/Control/Reaper/Internal.hs 1970-01-01
01:00:00.000000000 +0100
+++ new/auto-update-0.2.2/Control/Reaper/Internal.hs 2001-09-09
03:46:40.000000000 +0200
@@ -0,0 +1,28 @@
+module Control.Reaper.Internal (Reaper (..)) where
+
+-- | A data structure to hold reaper APIs.
+data Reaper workload item = Reaper
+ { reaperAdd :: item -> IO ()
+ -- ^ Adding an item to the workload
+ , reaperRead :: IO workload
+ -- ^ Reading workload.
+ , reaperModify :: (workload -> workload) -> IO workload
+ -- ^ Modify the workload. The resulting workload is returned.
+ --
+ -- If there is no reaper thread, the modifier will not be applied and
+ -- 'reaperEmpty' will be returned.
+ --
+ -- If the reaper is currently executing jobs, those jobs will not be in
+ -- the given workload and the workload might appear empty.
+ --
+ -- If all jobs are removed by the modifier, the reaper thread will not be
+ -- killed. The reaper thread will only terminate if 'reaperKill' is
called
+ -- or the result of 'reaperAction' satisfies 'reaperNull'.
+ --
+ -- @since 0.2.0
+ , reaperStop :: IO workload
+ -- ^ Stopping the reaper thread if exists.
+ -- The current workload is returned.
+ , reaperKill :: IO ()
+ -- ^ Killing the reaper thread immediately if exists.
+ }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/auto-update-0.1.6/Control/Reaper.hs
new/auto-update-0.2.2/Control/Reaper.hs
--- old/auto-update-0.1.6/Control/Reaper.hs 2019-02-20 10:43:14.000000000
+0100
+++ new/auto-update-0.2.2/Control/Reaper.hs 2001-09-09 03:46:40.000000000
+0200
@@ -1,5 +1,5 @@
-{-# LANGUAGE RecordWildCards #-}
-{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE RecordWildCards #-}
-- | This module provides the ability to create reapers: dedicated cleanup
-- threads. These threads will automatically spawn and die based on the
@@ -12,30 +12,42 @@
-- For real-world usage, search the <https://github.com/yesodweb/wai WAI
family of packages>
-- for imports of "Control.Reaper".
module Control.Reaper (
- -- * Example: Regularly cleaning a cache
- -- $example1
+ -- * Example: Regularly cleaning a cache
+ -- $example1
- -- * Settings
- ReaperSettings
- , defaultReaperSettings
- -- * Accessors
- , reaperAction
- , reaperDelay
- , reaperCons
- , reaperNull
- , reaperEmpty
- -- * Type
- , Reaper(..)
- -- * Creation
- , mkReaper
- -- * Helper
- , mkListAction
- ) where
+ -- * Settings
+ ReaperSettings,
+ defaultReaperSettings,
+
+ -- * Accessors
+ reaperAction,
+ reaperDelay,
+ reaperCons,
+ reaperNull,
+ reaperEmpty,
+ reaperThreadName,
+
+ -- * Type
+ Reaper,
+ reaperAdd,
+ reaperRead,
+ reaperModify,
+ reaperStop,
+ reaperKill,
+
+ -- * Creation
+ mkReaper,
+
+ -- * Helper
+ mkListAction,
+) where
import Control.AutoUpdate.Util (atomicModifyIORef')
-import Control.Concurrent (forkIO, threadDelay, killThread, ThreadId)
+import Control.Concurrent (ThreadId, forkIO, killThread, threadDelay)
import Control.Exception (mask_)
+import Control.Reaper.Internal
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
+import GHC.Conc.Sync (labelThread)
-- | Settings for creating a reaper. This type has two parameters:
-- @workload@ gives the entire workload, whereas @item@ gives an
@@ -83,6 +95,7 @@
-- Default: empty list.
--
-- @since 0.1.1
+ , reaperThreadName :: String
}
-- | Default @ReaperSettings@ value, biased towards having a list of work
@@ -90,30 +103,22 @@
--
-- @since 0.1.1
defaultReaperSettings :: ReaperSettings [item] item
-defaultReaperSettings = ReaperSettings
- { reaperAction = \wl -> return (wl ++)
- , reaperDelay = 30000000
- , reaperCons = (:)
- , reaperNull = null
- , reaperEmpty = []
- }
-
--- | A data structure to hold reaper APIs.
-data Reaper workload item = Reaper {
- -- | Adding an item to the workload
- reaperAdd :: item -> IO ()
- -- | Reading workload.
- , reaperRead :: IO workload
- -- | Stopping the reaper thread if exists.
- -- The current workload is returned.
- , reaperStop :: IO workload
- -- | Killing the reaper thread immediately if exists.
- , reaperKill :: IO ()
- }
+defaultReaperSettings =
+ ReaperSettings
+ { reaperAction = \wl -> return (wl ++)
+ , reaperDelay = 30000000
+ , reaperCons = (:)
+ , reaperNull = null
+ , reaperEmpty = []
+ , reaperThreadName = "Reaper"
+ }
-- | State of reaper.
-data State workload = NoReaper -- ^ No reaper thread
- | Workload !workload -- ^ The current jobs
+data State workload
+ = -- | No reaper thread
+ NoReaper
+ | -- | The current jobs
+ Workload !workload
-- | Create a reaper addition function. This function can be used to add
-- new items to the workload. Spawning of reaper threads will be handled
@@ -123,52 +128,71 @@
mkReaper :: ReaperSettings workload item -> IO (Reaper workload item)
mkReaper settings@ReaperSettings{..} = do
stateRef <- newIORef NoReaper
- tidRef <- newIORef Nothing
- return Reaper {
- reaperAdd = add settings stateRef tidRef
- , reaperRead = readRef stateRef
- , reaperStop = stop stateRef
- , reaperKill = kill tidRef
- }
+ tidRef <- newIORef Nothing
+ return
+ Reaper
+ { reaperAdd = add settings stateRef tidRef
+ , reaperRead = readRef stateRef
+ , reaperModify = modifyRef stateRef
+ , reaperStop = stop stateRef
+ , reaperKill = kill tidRef
+ }
where
readRef stateRef = do
mx <- readIORef stateRef
case mx of
- NoReaper -> return reaperEmpty
+ NoReaper -> return reaperEmpty
Workload wl -> return wl
+ modifyRef stateRef modifier = atomicModifyIORef' stateRef $ \mx ->
+ case mx of
+ NoReaper ->
+ (NoReaper, reaperEmpty)
+ Workload wl ->
+ let !wl' = modifier wl
+ in (Workload wl', wl')
stop stateRef = atomicModifyIORef' stateRef $ \mx ->
case mx of
- NoReaper -> (NoReaper, reaperEmpty)
+ NoReaper -> (NoReaper, reaperEmpty)
Workload x -> (Workload reaperEmpty, x)
kill tidRef = do
mtid <- readIORef tidRef
case mtid of
- Nothing -> return ()
+ Nothing -> return ()
Just tid -> killThread tid
-add :: ReaperSettings workload item
- -> IORef (State workload) -> IORef (Maybe ThreadId)
- -> item -> IO ()
+add
+ :: ReaperSettings workload item
+ -> IORef (State workload)
+ -> IORef (Maybe ThreadId)
+ -> item
+ -> IO ()
add settings@ReaperSettings{..} stateRef tidRef item =
mask_ $ do
- next <- atomicModifyIORef' stateRef cons
- next
+ next <- atomicModifyIORef' stateRef cons
+ next
where
- cons NoReaper = let wl = reaperCons item reaperEmpty
- in (Workload wl, spawn settings stateRef tidRef)
- cons (Workload wl) = let wl' = reaperCons item wl
- in (Workload wl', return ())
-
-spawn :: ReaperSettings workload item
- -> IORef (State workload) -> IORef (Maybe ThreadId)
- -> IO ()
+ cons NoReaper =
+ let wl = reaperCons item reaperEmpty
+ in (Workload wl, spawn settings stateRef tidRef)
+ cons (Workload wl) =
+ let wl' = reaperCons item wl
+ in (Workload wl', return ())
+
+spawn
+ :: ReaperSettings workload item
+ -> IORef (State workload)
+ -> IORef (Maybe ThreadId)
+ -> IO ()
spawn settings stateRef tidRef = do
tid <- forkIO $ reaper settings stateRef tidRef
+ labelThread tid $ reaperThreadName settings
writeIORef tidRef $ Just tid
-reaper :: ReaperSettings workload item
- -> IORef (State workload) -> IORef (Maybe ThreadId)
- -> IO ()
+reaper
+ :: ReaperSettings workload item
+ -> IORef (State workload)
+ -> IORef (Maybe ThreadId)
+ -> IO ()
reaper settings@ReaperSettings{..} stateRef tidRef = do
threadDelay reaperDelay
-- Getting the current jobs. Push an empty job to the reference.
@@ -181,15 +205,15 @@
next <- atomicModifyIORef' stateRef (check merge)
next
where
- swapWithEmpty NoReaper = error "Control.Reaper.reaper: unexpected
NoReaper (1)"
+ swapWithEmpty NoReaper = error "Control.Reaper.reaper: unexpected NoReaper
(1)"
swapWithEmpty (Workload wl) = (Workload reaperEmpty, wl)
- check _ NoReaper = error "Control.Reaper.reaper: unexpected NoReaper (2)"
+ check _ NoReaper = error "Control.Reaper.reaper: unexpected NoReaper (2)"
check merge (Workload wl)
- -- If there is no job, reaper is terminated.
- | reaperNull wl' = (NoReaper, writeIORef tidRef Nothing)
- -- If there are jobs, carry them out.
- | otherwise = (Workload wl', reaper settings stateRef tidRef)
+ -- If there is no job, reaper is terminated.
+ | reaperNull wl' = (NoReaper, writeIORef tidRef Nothing)
+ -- If there are jobs, carry them out.
+ | otherwise = (Workload wl', reaper settings stateRef tidRef)
where
wl' = merge wl
@@ -199,24 +223,38 @@
-- expired.
--
-- @since 0.1.1
-mkListAction :: (item -> IO (Maybe item'))
- -> [item]
- -> IO ([item'] -> [item'])
+mkListAction
+ :: (item -> IO (Maybe item'))
+ -> [item]
+ -> IO ([item'] -> [item'])
mkListAction f =
go id
where
go !front [] = return front
- go !front (x:xs) = do
+ go !front (x : xs) = do
my <- f x
let front' =
case my of
Nothing -> front
- Just y -> front . (y:)
+ Just y -> front . (y :)
go front' xs
-- $example1
-- In this example code, we use a 'Data.Map.Strict.Map' to cache fibonacci
numbers, and a 'Reaper' to prune the cache.
--
+-- NOTE: When using this module as a cache you should keep in mind that while
+-- the reaper thread is active running your "reaperAction", the cache will
+-- appear empty to concurrently running threads. Any newly created cache
+-- entries will be on the temporary worklist, and will merged back into the the
+-- main cache only once the "reaperAction" completes (together with the portion
+-- of the extant worklist that the @cleaner@ callback decided to retain).
+--
+-- If you're looking for a cache that supports concurrent purging of stale
+-- items, but without exposing a transient empty cache during cleanup, this is
+-- not the cache implementation you need. This module was primarily designed
+-- for cleaning up /stuck/ processes, or idle threads in a thread pool. The
cache
+-- use-case was not a primary design focus.
+--
-- The @main@ function first creates a 'Reaper', with fields to initialize the
-- cache ('reaperEmpty'), add items to it ('reaperCons'), and prune it
('reaperAction').
-- The reaper will run every two seconds ('reaperDelay'), but will stop
running while
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/auto-update-0.1.6/Setup.hs
new/auto-update-0.2.2/Setup.hs
--- old/auto-update-0.1.6/Setup.hs 2015-11-09 03:12:10.000000000 +0100
+++ new/auto-update-0.2.2/Setup.hs 2001-09-09 03:46:40.000000000 +0200
@@ -1,2 +1,3 @@
import Distribution.Simple
+
main = defaultMain
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/auto-update-0.1.6/auto-update.cabal
new/auto-update-0.2.2/auto-update.cabal
--- old/auto-update-0.1.6/auto-update.cabal 2019-07-09 09:40:38.000000000
+0200
+++ new/auto-update-0.2.2/auto-update.cabal 2001-09-09 03:46:40.000000000
+0200
@@ -1,5 +1,5 @@
name: auto-update
-version: 0.1.6
+version: 0.2.2
synopsis: Efficiently run periodic, on-demand actions
description: API docs and the README are available at
<http://www.stackage.org/package/auto-update>.
homepage: https://github.com/yesodweb/wai
@@ -19,8 +19,9 @@
Control.Debounce
Control.Debounce.Internal
Control.Reaper
+ Control.Reaper.Internal
other-modules: Control.AutoUpdate.Util
- build-depends: base >= 4 && < 5
+ build-depends: base >= 4.12 && < 5
default-language: Haskell2010
if impl(ghc >= 8)
default-extensions: Strict StrictData
@@ -35,4 +36,5 @@
hs-source-dirs: test
type: exitcode-stdio-1.0
build-depends: base, auto-update, exceptions, hspec, retry, HUnit
+ build-tool-depends: hspec-discover:hspec-discover
default-language: Haskell2010
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/auto-update-0.1.6/test/Control/AutoUpdateSpec.hs
new/auto-update-0.2.2/test/Control/AutoUpdateSpec.hs
--- old/auto-update-0.1.6/test/Control/AutoUpdateSpec.hs 2019-07-09
09:40:38.000000000 +0200
+++ new/auto-update-0.2.2/test/Control/AutoUpdateSpec.hs 2001-09-09
03:46:40.000000000 +0200
@@ -1,35 +1,37 @@
module Control.AutoUpdateSpec (spec) where
-import Control.AutoUpdate
-import Control.Concurrent (threadDelay)
-import Control.Monad (replicateM_, forM_)
-import Data.IORef
+-- import Control.AutoUpdate
+-- import Control.Concurrent (threadDelay)
+-- import Control.Monad (replicateM_, forM_)
+-- import Data.IORef
import Test.Hspec
-import Test.Hspec.QuickCheck
+
+-- import Test.Hspec.QuickCheck
spec :: Spec
spec = return ()
- -- do
- -- prop "incrementer" $ \st' -> do
- -- let st = abs st' `mod` 10000
- -- 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
+
+-- do
+-- prop "incrementer" $ \st' -> do
+-- let st = abs st' `mod` 10000
+-- 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/auto-update-0.1.6/test/Control/DebounceSpec.hs
new/auto-update-0.2.2/test/Control/DebounceSpec.hs
--- old/auto-update-0.1.6/test/Control/DebounceSpec.hs 2019-07-09
09:40:38.000000000 +0200
+++ new/auto-update-0.2.2/test/Control/DebounceSpec.hs 2001-09-09
03:46:40.000000000 +0200
@@ -1,5 +1,4 @@
-{-# LANGUAGE ScopedTypeVariables #-}
-module Control.DebounceSpec (spec) where
+module Control.DebounceSpec (main, spec) where
import Control.Concurrent
import Control.Debounce
@@ -15,22 +14,22 @@
spec = describe "mkDebounce" $ do
describe "Leading edge" $ do
it "works for a single event" $ do
- (ref, debounced, baton, returnFromWait) <- getDebounce leadingEdge
+ (ref, debounced, _baton, returnFromWait) <- getDebounce leadingEdge
debounced
- waitUntil 5 $ readIORef ref >>= (`shouldBe` 1)
+ waitUntil 5 $ readIORef ref `shouldReturn` 1
returnFromWait
pause
- readIORef ref >>= (`shouldBe` 1)
+ readIORef ref `shouldReturn` 1
-- Try another round
debounced
- waitUntil 5 $ readIORef ref >>= (`shouldBe` 2)
+ waitUntil 5 $ readIORef ref `shouldReturn` 2
returnFromWait
pause
- readIORef ref >>= (`shouldBe` 2)
+ readIORef ref `shouldReturn` 2
it "works for multiple events" $ do
(ref, debounced, baton, returnFromWait) <- getDebounce leadingEdge
@@ -39,30 +38,30 @@
waitForBatonToBeTaken baton
debounced
pause
- waitUntil 5 $ readIORef ref >>= (`shouldBe` 1)
+ waitUntil 5 $ readIORef ref `shouldReturn` 1
returnFromWait
pause
- readIORef ref >>= (`shouldBe` 2)
+ readIORef ref `shouldReturn` 2
describe "Trailing edge" $ do
it "works for a single event" $ do
- (ref, debounced, baton, returnFromWait) <- getDebounce trailingEdge
+ (ref, debounced, _baton, returnFromWait) <- getDebounce
trailingEdge
debounced
pause
- waitUntil 5 $ readIORef ref >>= (`shouldBe` 0)
+ waitUntil 5 $ readIORef ref `shouldReturn` 0
returnFromWait
- waitUntil 5 $ readIORef ref >>= (`shouldBe` 1)
+ waitUntil 5 $ readIORef ref `shouldReturn` 1
-- Try another round
debounced
pause
- waitUntil 5 $ readIORef ref >>= (`shouldBe` 1)
+ waitUntil 5 $ readIORef ref `shouldReturn` 1
returnFromWait
- waitUntil 5 $ readIORef ref >>= (`shouldBe` 2)
+ waitUntil 5 $ readIORef ref `shouldReturn` 2
it "works for multiple events" $ do
(ref, debounced, baton, returnFromWait) <- getDebounce trailingEdge
@@ -71,11 +70,10 @@
waitForBatonToBeTaken baton
debounced
pause
- waitUntil 5 $ readIORef ref >>= (`shouldBe` 0)
+ waitUntil 5 $ readIORef ref `shouldReturn` 0
returnFromWait
- waitUntil 5 $ readIORef ref >>= (`shouldBe` 1)
-
+ waitUntil 5 $ readIORef ref `shouldReturn` 1
-- | Make a controllable delay function
getWaitAction :: IO (p -> IO (), IO ())
@@ -88,33 +86,38 @@
-- | Get a debounce system with access to the internals for testing
getDebounce :: DI.DebounceEdge -> IO (IORef Int, IO (), MVar (), IO ())
getDebounce edge = do
- ref :: IORef Int <- newIORef 0
- let action = modifyIORef ref (+ 1)
+ ref <- newIORef 0
+ let action = modifyIORef ref (+ 1)
- (waitAction, returnFromWait) <- getWaitAction
+ (waitAction, returnFromWait) <- getWaitAction
- baton <- newEmptyMVar
+ baton <- newEmptyMVar
- debounced <- DI.mkDebounceInternal baton waitAction defaultDebounceSettings {
- debounceFreq = 5000000 -- unused
- , debounceAction = action
- , debounceEdge = edge
- }
+ debounced <-
+ DI.mkDebounceInternal
+ baton
+ waitAction
+ defaultDebounceSettings
+ { debounceFreq = 5000000 -- unused
+ , debounceAction = action
+ , debounceEdge = edge
+ }
- return (ref, debounced, baton, returnFromWait)
+ return (ref, debounced, baton, returnFromWait)
-- | Pause briefly (100ms)
pause :: IO ()
pause = threadDelay 100000
waitForBatonToBeTaken :: MVar () -> IO ()
-waitForBatonToBeTaken baton = waitUntil 5 $ tryReadMVar baton >>= (`shouldBe`
Nothing)
+waitForBatonToBeTaken baton = waitUntil 5 $ tryReadMVar baton `shouldReturn`
Nothing
-- | Wait up to n seconds for an action to complete without throwing an
HUnitFailure
waitUntil :: Int -> IO a -> IO ()
waitUntil n action = recovering policy [handler] (\_status -> void action)
- where policy = constantDelay 1000 `mappend` limitRetries (n * 1000) -- 1ms *
n * 1000 tries = n seconds
- handler _status = Handler (\(HUnitFailure {}) -> return True)
+ where
+ policy = constantDelay 1000 `mappend` limitRetries (n * 1000) -- 1ms * n *
1000 tries = n seconds
+ handler _status = Handler (\HUnitFailure{} -> return True)
main :: IO ()
main = hspec spec
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/auto-update-0.1.6/test/Control/ReaperSpec.hs
new/auto-update-0.2.2/test/Control/ReaperSpec.hs
--- old/auto-update-0.1.6/test/Control/ReaperSpec.hs 2019-07-09
09:40:38.000000000 +0200
+++ new/auto-update-0.2.2/test/Control/ReaperSpec.hs 2001-09-09
03:46:40.000000000 +0200
@@ -1,14 +1,15 @@
module Control.ReaperSpec (spec) where
-import Control.Concurrent
-import Control.Reaper
-import Data.IORef
+-- import Control.Concurrent
+-- import Control.Reaper
+-- import Data.IORef
import Test.Hspec
-import Test.Hspec.QuickCheck
+-- import Test.Hspec.QuickCheck
spec :: Spec
spec = return ()
+
-- prop "works" $ \is -> do
-- reaper <- mkReaper defaultReaperSettings
-- { reaperAction = action