packages feed

async-pool 0.9.0 → 0.9.0.1

raw patch · 3 files changed

+53/−16 lines, 3 filesdep ~basePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: base

API changes (from Hackage documentation)

- Control.Concurrent.Async.Pool: runConcurrently :: Concurrently a -> TaskGroup -> IO a
+ Control.Concurrent.Async.Pool: [runConcurrently] :: Concurrently a -> TaskGroup -> IO a

Files

Control/Concurrent/Async/Pool/Async.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, MagicHash, UnboxedTuples, RankNTypes #-}+{-# LANGUAGE CPP, MagicHash, UnboxedTuples, RankNTypes, GADTs #-} #if __GLASGOW_HASKELL__ >= 701 {-# LANGUAGE Trustworthy #-} #endif@@ -107,10 +107,21 @@  -- | A 'Handle' is a unique identifier for a task submitted to a 'Pool'. type Handle    = Node-data State     = Ready | Starting | Started ThreadId deriving (Eq, Show)+data State     = Ready | Starting | forall a. Started ThreadId (TMVar a) data Status    = Pending | Completed deriving (Eq, Show) type TaskGraph = Gr (TVar State) Status +instance Eq State where+    Ready        == Ready        = True+    Starting     == Starting     = True+    Started n1 _ == Started n2 _ = n1 == n2+    _            == _            = False++instance Show State where+    show Ready         = "Ready"+    show Starting      = "Starting"+    show (Started n _) = "Started " ++ show n+ -- | A 'Pool' manages a collection of possibly interdependent tasks, such that --   tasks await execution until the tasks they depend on have finished (and --   tasks may depend on an arbitrary number of other tasks), while@@ -141,11 +152,25 @@       -- ^ Tokens identify tasks, and are provisioned monotonically.     } +waitTMVar :: TMVar a -> STM ()+waitTMVar tv = do+    _ <- readTMVar tv+    return ()++syncPool :: Pool -> STM ()+syncPool p = do+    g <- readTVar (tasks p)+    forM_ (labNodes g) $ \(_h, st) -> do+        x <- readTVar st+        case x of+            Started _tid v -> waitTMVar v+            _ -> retry+ data TaskGroup = TaskGroup     { pool    :: Pool     , avail   :: TVar Int       -- ^ The number of available execution slots in the pool.-    , pending :: TVar (IntMap (IO ThreadId))+    , pending :: forall a. TVar (IntMap (IO ThreadId, TMVar a))       -- ^ Nodes in the task graph that are waiting to start.     } @@ -171,9 +196,9 @@ getThreadId g h = do     status <- readTVar (getTaskVar g h)     case status of-        Ready     -> return Nothing-        Starting  -> retry-        Started x -> return $ Just x+        Ready       -> return Nothing+        Starting    -> retry+        Started x _ -> return $ Just x  instance Eq (Async a) where   Async _ a _ == Async _ b _  =  a == b@@ -218,7 +243,7 @@             doFork $ try (restore (action `finally` cleanup h))                 >>= atomically . putTMVar var -    modifyTVar (pending p) (IntMap.insert h start)+    modifyTVar (pending p) (IntMap.insert h (start, var))     tv <- newTVar Ready     modifyTVar (tasks (pool p)) (insNode (h, tv)) 
Control/Concurrent/Async/Pool/Internal.hs view
@@ -23,10 +23,11 @@ import           Data.Monoid (Monoid(mempty), (<>)) import           Data.Traversable (Traversable(sequenceA), forM) import           Prelude hiding (mapM_, mapM, foldr, all, any, concatMap, foldl1)+import           Unsafe.Coerce  -- | Return a list of actions ready for execution, by checking the graph to --   ensure all dependencies have completed.-getReadyNodes :: TaskGroup -> TaskGraph -> STM (IntMap (IO ThreadId))+getReadyNodes :: TaskGroup -> TaskGraph -> STM (IntMap (IO ThreadId, TMVar a)) getReadyNodes p g = do     availSlots <- readTVar (avail p)     check (availSlots > 0)@@ -49,7 +50,7 @@  -- | Return a list of tasks ready to execute, and their related state --   variables from the dependency graph.-getReadyTasks :: TaskGroup -> STM [(TVar State, IO ThreadId)]+getReadyTasks :: TaskGroup -> STM [(TVar State, (IO ThreadId, TMVar a))] getReadyTasks p = do     g <- readTVar (tasks (pool p))     map (first (getTaskVar g)) . IntMap.toList <$> getReadyNodes p g@@ -60,12 +61,23 @@ createPool = Pool <$> newTVarIO Gr.empty                   <*> newTVarIO 0 +-- | Use a task pool for a bounded region. At the end of the region,+-- 'withPool' will block until all tasks have completed.+withPool :: (Pool -> IO a) -> IO a+withPool f = do+    p <- createPool+    x <- f p+    atomically $ syncPool p+    return x+ -- | Create a task group for executing interdependent tasks concurrently.  The --   number of available slots governs how many tasks may run at one time. createTaskGroup :: Pool -> Int -> IO TaskGroup-createTaskGroup p cnt = TaskGroup <$> pure p-                                  <*> newTVarIO cnt-                                  <*> newTVarIO mempty+createTaskGroup p cnt = do+    c <- newTVarIO cnt+    m <- newTVarIO IntMap.empty+    -- Prior to GHC 8, this call to unsafeCoerce was not necessary.+    return $ TaskGroup p c (unsafeCoerce m)  -- | Execute tasks in a given task group.  The number of slots determines how --   many threads may execute concurrently.@@ -78,9 +90,9 @@         check (not (null ready))         forM_ ready $ \(tv, _) -> writeTVar tv Starting         return ready-    forM_ ready $ \(tv, go) -> do+    forM_ ready $ \(tv, (go, var)) -> do         t <- go-        atomically $ swapTVar tv $ Started t+        atomically $ swapTVar tv $ Started t var  -- | Create a task group within the given pool having a specified number of --   execution slots, but with a bounded lifetime.  Leaving the block cancels
async-pool.cabal view
@@ -1,5 +1,5 @@ Name:           async-pool-Version:        0.9.0+Version:        0.9.0.1 Synopsis:       A modified version of async that supports worker groups and many-to-many task dependencies License-file:   LICENSE License:        MIT@@ -20,7 +20,7 @@     default-language: Haskell98     ghc-options:      -Wall     build-depends:-        base                 >= 3 && < 5+        base >= 3 && < 4.10       , fgl       , async       , stm