{-# 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.Concurrent (ThreadId, forkIO, killThread, threadDelay)
import Control.Exception (mask_)
import Control.Reaper.Internal
import Data.IORef (IORef, atomicModifyIORef', 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
    
    
    Bool
cont <- IORef (State workload)
-> (State workload -> (State workload, Bool)) -> IO Bool
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef (State workload)
stateRef ((workload -> workload) -> State workload -> (State workload, Bool)
forall {workload}.
(workload -> workload) -> State workload -> (State workload, Bool)
check workload -> workload
merge)
    if Bool
cont
        then
            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
        else
            IORef (Maybe ThreadId) -> Maybe ThreadId -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef IORef (Maybe ThreadId)
tidRef Maybe ThreadId
forall a. Maybe a
Nothing
  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, Bool)
check workload -> workload
_ State workload
NoReaper = String -> (State workload, Bool)
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, Bool
False)
        
        | Bool
otherwise = (workload -> State workload
forall workload. workload -> State workload
Workload workload
wl', Bool
True)
      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