{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE RecordWildCards #-}
module Control.Reaper (
ReaperSettings,
defaultReaperSettings,
reaperAction,
reaperDelay,
reaperCons,
reaperNull,
reaperEmpty,
reaperThreadName,
Reaper,
reaperAdd,
reaperRead,
reaperModify,
reaperStop,
reaperKill,
mkReaper,
mkListAction,
) where
import Control.AutoUpdate.Util (atomicModifyIORef')
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)
data ReaperSettings workload item = ReaperSettings
{ forall workload item.
ReaperSettings workload item
-> workload -> IO (workload -> workload)
reaperAction :: workload -> IO (workload -> workload)
, forall workload item. ReaperSettings workload item -> Int
reaperDelay :: {-# UNPACK #-} !Int
, forall workload item.
ReaperSettings workload item -> item -> workload -> workload
reaperCons :: item -> workload -> workload
, forall workload item.
ReaperSettings workload item -> workload -> Bool
reaperNull :: workload -> Bool
, forall workload item. ReaperSettings workload item -> workload
reaperEmpty :: workload
, forall workload item. ReaperSettings workload item -> String
reaperThreadName :: String
}
defaultReaperSettings :: ReaperSettings [item] item
defaultReaperSettings :: forall item. ReaperSettings [item] item
defaultReaperSettings =
ReaperSettings
{ reaperAction :: [item] -> IO ([item] -> [item])
reaperAction = \[item]
wl -> ([item] -> [item]) -> IO ([item] -> [item])
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ([item]
wl [item] -> [item] -> [item]
forall a. [a] -> [a] -> [a]
++)
, reaperDelay :: Int
reaperDelay = Int
30000000
, reaperCons :: item -> [item] -> [item]
reaperCons = (:)
, reaperNull :: [item] -> Bool
reaperNull = [item] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null
, reaperEmpty :: [item]
reaperEmpty = []
, reaperThreadName :: String
reaperThreadName = String
"Reaper"
}
data State workload
=
NoReaper
|
Workload !workload
mkReaper :: ReaperSettings workload item -> IO (Reaper workload item)
mkReaper :: forall workload item.
ReaperSettings workload item -> IO (Reaper workload item)
mkReaper settings :: ReaperSettings workload item
settings@ReaperSettings{workload
Int
String
workload -> Bool
workload -> IO (workload -> workload)
item -> workload -> workload
reaperAction :: forall workload item.
ReaperSettings workload item
-> workload -> IO (workload -> workload)
reaperDelay :: forall workload item. ReaperSettings workload item -> Int
reaperCons :: forall workload item.
ReaperSettings workload item -> item -> workload -> workload
reaperNull :: forall workload item.
ReaperSettings workload item -> workload -> Bool
reaperEmpty :: forall workload item. ReaperSettings workload item -> workload
reaperThreadName :: forall workload item. ReaperSettings workload item -> String
reaperAction :: workload -> IO (workload -> workload)
reaperDelay :: Int
reaperCons :: item -> workload -> workload
reaperNull :: workload -> Bool
reaperEmpty :: workload
reaperThreadName :: String
..} = do
IORef (State workload)
stateRef <- State workload -> IO (IORef (State workload))
forall a. a -> IO (IORef a)
newIORef State workload
forall workload. State workload
NoReaper
IORef (Maybe ThreadId)
tidRef <- Maybe ThreadId -> IO (IORef (Maybe ThreadId))
forall a. a -> IO (IORef a)
newIORef Maybe ThreadId
forall a. Maybe a
Nothing
Reaper workload item -> IO (Reaper workload item)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return
Reaper
{ reaperAdd :: item -> IO ()
reaperAdd = ReaperSettings workload item
-> IORef (State workload)
-> IORef (Maybe ThreadId)
-> item
-> IO ()
forall workload item.
ReaperSettings workload item
-> IORef (State workload)
-> IORef (Maybe ThreadId)
-> item
-> IO ()
add ReaperSettings workload item
settings IORef (State workload)
stateRef IORef (Maybe ThreadId)
tidRef
, reaperRead :: IO workload
reaperRead = IORef (State workload) -> IO workload
readRef IORef (State workload)
stateRef
, reaperModify :: (workload -> workload) -> IO workload
reaperModify = IORef (State workload) -> (workload -> workload) -> IO workload
modifyRef IORef (State workload)
stateRef
, reaperStop :: IO workload
reaperStop = IORef (State workload) -> IO workload
stop IORef (State workload)
stateRef
, reaperKill :: IO ()
reaperKill = IORef (Maybe ThreadId) -> IO ()
kill IORef (Maybe ThreadId)
tidRef
}
where
readRef :: IORef (State workload) -> IO workload
readRef IORef (State workload)
stateRef = do
State workload
mx <- IORef (State workload) -> IO (State workload)
forall a. IORef a -> IO a
readIORef IORef (State workload)
stateRef
case State workload
mx of
State workload
NoReaper -> workload -> IO workload
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return workload
reaperEmpty
Workload workload
wl -> workload -> IO workload
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return workload
wl
modifyRef :: IORef (State workload) -> (workload -> workload) -> IO workload
modifyRef IORef (State workload)
stateRef workload -> workload
modifier = IORef (State workload)
-> (State workload -> (State workload, workload)) -> IO workload
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef (State workload)
stateRef ((State workload -> (State workload, workload)) -> IO workload)
-> (State workload -> (State workload, workload)) -> IO workload
forall a b. (a -> b) -> a -> b
$ \State workload
mx ->
case State workload
mx of
State workload
NoReaper ->
(State workload
forall workload. State workload
NoReaper, workload
reaperEmpty)
Workload workload
wl ->
let !wl' :: workload
wl' = workload -> workload
modifier workload
wl
in (workload -> State workload
forall workload. workload -> State workload
Workload workload
wl', workload
wl')
stop :: IORef (State workload) -> IO workload
stop IORef (State workload)
stateRef = IORef (State workload)
-> (State workload -> (State workload, workload)) -> IO workload
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef (State workload)
stateRef ((State workload -> (State workload, workload)) -> IO workload)
-> (State workload -> (State workload, workload)) -> IO workload
forall a b. (a -> b) -> a -> b
$ \State workload
mx ->
case State workload
mx of
State workload
NoReaper -> (State workload
forall workload. State workload
NoReaper, workload
reaperEmpty)
Workload workload
x -> (workload -> State workload
forall workload. workload -> State workload
Workload workload
reaperEmpty, workload
x)
kill :: IORef (Maybe ThreadId) -> IO ()
kill IORef (Maybe ThreadId)
tidRef = do
Maybe ThreadId
mtid <- IORef (Maybe ThreadId) -> IO (Maybe ThreadId)
forall a. IORef a -> IO a
readIORef IORef (Maybe ThreadId)
tidRef
case Maybe ThreadId
mtid of
Maybe ThreadId
Nothing -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just ThreadId
tid -> ThreadId -> IO ()
killThread ThreadId
tid
add
:: ReaperSettings workload item
-> IORef (State workload)
-> IORef (Maybe ThreadId)
-> item
-> IO ()
add :: forall workload item.
ReaperSettings workload item
-> IORef (State workload)
-> IORef (Maybe ThreadId)
-> item
-> IO ()
add settings :: ReaperSettings workload item
settings@ReaperSettings{workload
Int
String
workload -> Bool
workload -> IO (workload -> workload)
item -> workload -> workload
reaperAction :: forall workload item.
ReaperSettings workload item
-> workload -> IO (workload -> workload)
reaperDelay :: forall workload item. ReaperSettings workload item -> Int
reaperCons :: forall workload item.
ReaperSettings workload item -> item -> workload -> workload
reaperNull :: forall workload item.
ReaperSettings workload item -> workload -> Bool
reaperEmpty :: forall workload item. ReaperSettings workload item -> workload
reaperThreadName :: forall workload item. ReaperSettings workload item -> String
reaperAction :: workload -> IO (workload -> workload)
reaperDelay :: Int
reaperCons :: item -> workload -> workload
reaperNull :: workload -> Bool
reaperEmpty :: workload
reaperThreadName :: String
..} IORef (State workload)
stateRef IORef (Maybe ThreadId)
tidRef item
item =
IO () -> IO ()
forall a. IO a -> IO a
mask_ (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
IO ()
next <- IORef (State workload)
-> (State workload -> (State workload, IO ())) -> IO (IO ())
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef (State workload)
stateRef State workload -> (State workload, IO ())
cons
IO ()
next
where
cons :: State workload -> (State workload, IO ())
cons State workload
NoReaper =
let wl :: workload
wl = item -> workload -> workload
reaperCons item
item workload
reaperEmpty
in (workload -> State workload
forall workload. workload -> State workload
Workload workload
wl, ReaperSettings workload item
-> IORef (State workload) -> IORef (Maybe ThreadId) -> IO ()
forall workload item.
ReaperSettings workload item
-> IORef (State workload) -> IORef (Maybe ThreadId) -> IO ()
spawn ReaperSettings workload item
settings IORef (State workload)
stateRef IORef (Maybe ThreadId)
tidRef)
cons (Workload workload
wl) =
let wl' :: workload
wl' = item -> workload -> workload
reaperCons item
item workload
wl
in (workload -> State workload
forall workload. workload -> State workload
Workload workload
wl', () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ())
spawn
:: ReaperSettings workload item
-> IORef (State workload)
-> IORef (Maybe ThreadId)
-> IO ()
spawn :: forall workload item.
ReaperSettings workload item
-> IORef (State workload) -> IORef (Maybe ThreadId) -> IO ()
spawn ReaperSettings workload item
settings IORef (State workload)
stateRef IORef (Maybe ThreadId)
tidRef = do
ThreadId
tid <- IO () -> IO ThreadId
forkIO (IO () -> IO ThreadId) -> IO () -> IO ThreadId
forall a b. (a -> b) -> a -> b
$ ReaperSettings workload item
-> IORef (State workload) -> IORef (Maybe ThreadId) -> IO ()
forall workload item.
ReaperSettings workload item
-> IORef (State workload) -> IORef (Maybe ThreadId) -> IO ()
reaper ReaperSettings workload item
settings IORef (State workload)
stateRef IORef (Maybe ThreadId)
tidRef
ThreadId -> String -> IO ()
labelThread ThreadId
tid (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ ReaperSettings workload item -> String
forall workload item. ReaperSettings workload item -> String
reaperThreadName ReaperSettings workload item
settings
IORef (Maybe ThreadId) -> Maybe ThreadId -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef IORef (Maybe ThreadId)
tidRef (Maybe ThreadId -> IO ()) -> Maybe ThreadId -> IO ()
forall a b. (a -> b) -> a -> b
$ ThreadId -> Maybe ThreadId
forall a. a -> Maybe a
Just ThreadId
tid
reaper
:: ReaperSettings workload item
-> IORef (State workload)
-> IORef (Maybe ThreadId)
-> IO ()
reaper :: forall workload item.
ReaperSettings workload item
-> IORef (State workload) -> IORef (Maybe ThreadId) -> IO ()
reaper settings :: ReaperSettings workload item
settings@ReaperSettings{workload
Int
String
workload -> Bool
workload -> IO (workload -> workload)
item -> workload -> workload
reaperAction :: forall workload item.
ReaperSettings workload item
-> workload -> IO (workload -> workload)
reaperDelay :: forall workload item. ReaperSettings workload item -> Int
reaperCons :: forall workload item.
ReaperSettings workload item -> item -> workload -> workload
reaperNull :: forall workload item.
ReaperSettings workload item -> workload -> Bool
reaperEmpty :: forall workload item. ReaperSettings workload item -> workload
reaperThreadName :: forall workload item. ReaperSettings workload item -> String
reaperAction :: workload -> IO (workload -> workload)
reaperDelay :: Int
reaperCons :: item -> workload -> workload
reaperNull :: workload -> Bool
reaperEmpty :: workload
reaperThreadName :: String
..} IORef (State workload)
stateRef IORef (Maybe ThreadId)
tidRef = do
Int -> IO ()
threadDelay Int
reaperDelay
workload
wl <- IORef (State workload)
-> (State workload -> (State workload, workload)) -> IO workload
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef (State workload)
stateRef State workload -> (State workload, workload)
forall {b}. State b -> (State workload, b)
swapWithEmpty
!workload -> workload
merge <- workload -> IO (workload -> workload)
reaperAction workload
wl
IO ()
next <- IORef (State workload)
-> (State workload -> (State workload, IO ())) -> IO (IO ())
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef (State workload)
stateRef ((workload -> workload) -> State workload -> (State workload, IO ())
forall {workload}.
(workload -> workload) -> State workload -> (State workload, IO ())
check workload -> workload
merge)
IO ()
next
where
swapWithEmpty :: State b -> (State workload, b)
swapWithEmpty State b
NoReaper = String -> (State workload, b)
forall a. HasCallStack => String -> a
error String
"Control.Reaper.reaper: unexpected NoReaper (1)"
swapWithEmpty (Workload b
wl) = (workload -> State workload
forall workload. workload -> State workload
Workload workload
reaperEmpty, b
wl)
check :: (workload -> workload) -> State workload -> (State workload, IO ())
check workload -> workload
_ State workload
NoReaper = String -> (State workload, IO ())
forall a. HasCallStack => String -> a
error String
"Control.Reaper.reaper: unexpected NoReaper (2)"
check workload -> workload
merge (Workload workload
wl)
| workload -> Bool
reaperNull workload
wl' = (State workload
forall workload. State workload
NoReaper, IORef (Maybe ThreadId) -> Maybe ThreadId -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef IORef (Maybe ThreadId)
tidRef Maybe ThreadId
forall a. Maybe a
Nothing)
| Bool
otherwise = (workload -> State workload
forall workload. workload -> State workload
Workload workload
wl', ReaperSettings workload item
-> IORef (State workload) -> IORef (Maybe ThreadId) -> IO ()
forall workload item.
ReaperSettings workload item
-> IORef (State workload) -> IORef (Maybe ThreadId) -> IO ()
reaper ReaperSettings workload item
settings IORef (State workload)
stateRef IORef (Maybe ThreadId)
tidRef)
where
wl' :: workload
wl' = workload -> workload
merge workload
wl
mkListAction
:: (item -> IO (Maybe item'))
-> [item]
-> IO ([item'] -> [item'])
mkListAction :: forall item item'.
(item -> IO (Maybe item')) -> [item] -> IO ([item'] -> [item'])
mkListAction item -> IO (Maybe item')
f =
([item'] -> [item']) -> [item] -> IO ([item'] -> [item'])
forall {c}. ([item'] -> c) -> [item] -> IO ([item'] -> c)
go [item'] -> [item']
forall a. a -> a
id
where
go :: ([item'] -> c) -> [item] -> IO ([item'] -> c)
go ![item'] -> c
front [] = ([item'] -> c) -> IO ([item'] -> c)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return [item'] -> c
front
go ![item'] -> c
front (item
x : [item]
xs) = do
Maybe item'
my <- item -> IO (Maybe item')
f item
x
let front' :: [item'] -> c
front' =
case Maybe item'
my of
Maybe item'
Nothing -> [item'] -> c
front
Just item'
y -> [item'] -> c
front ([item'] -> c) -> ([item'] -> [item']) -> [item'] -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (item'
y item' -> [item'] -> [item']
forall a. a -> [a] -> [a]
:)
([item'] -> c) -> [item] -> IO ([item'] -> c)
go [item'] -> c
front' [item]
xs