module Database.CQL.IO.Jobs
( Jobs
, new
, add
, destroy
, showJobs
) where
import Control.Applicative
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Monad
import Control.Monad.Catch
import Control.Monad.IO.Class
import Database.CQL.IO.Types
import Data.Map.Strict (Map)
import Data.Unique
import Prelude
import qualified Data.Map.Strict as Map
data Job
= Reserved
| Running !Unique !(Async ())
newtype Jobs k = Jobs (TVar (Map k Job))
new :: MonadIO m => m (Jobs k)
new = liftIO $ Jobs <$> newTVarIO Map.empty
add :: (MonadIO m, Ord k) => Jobs k -> k -> Bool -> IO () -> m ()
add (Jobs d) k replace j = liftIO $ do
(ok, prev) <- atomically $ do
m <- readTVar d
case Map.lookup k m of
Nothing -> do
modifyTVar' d (Map.insert k Reserved)
return (True, Nothing)
Just (Running _ a) | replace -> do
modifyTVar' d (Map.insert k Reserved)
return (True, Just a)
_ -> return (False, Nothing)
when ok $ do
maybe (return ()) (ignore . cancel) prev
u <- newUnique
a <- async $ j `finally` remove u
atomically $
modifyTVar' d (Map.insert k (Running u a))
where
remove u = atomically $
modifyTVar' d $ flip Map.update k $ \a ->
case a of
Running u' _ | u == u' -> Nothing
_ -> Just a
destroy :: MonadIO m => Jobs k -> m ()
destroy (Jobs d) = liftIO $ do
items <- Map.elems <$> atomically (swapTVar d Map.empty)
mapM_ f items
where
f (Running _ a) = ignore (cancel a)
f _ = return ()
showJobs :: MonadIO m => Jobs k -> m [k]
showJobs (Jobs d) = liftIO $ Map.keys <$> readTVarIO d