packages feed

async-pool 0.9.1 → 0.9.2

raw patch · 5 files changed

+43/−15 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Control.Concurrent.Async.Pool: cancelAll :: TaskGroup -> IO ()

Files

Control/Concurrent/Async/Pool.hs view
@@ -99,7 +99,7 @@     withAsyncOnWithUnmask,      -- ** Quering 'Async's-    wait, poll, waitCatch, cancel, cancelWith,+    wait, poll, waitCatch, cancel, cancelWith, cancelAll,      -- ** STM operations     waitSTM, pollSTM, waitCatchSTM,
Control/Concurrent/Async/Pool/Async.hs view
@@ -107,7 +107,7 @@  -- | A 'Handle' is a unique identifier for a task submitted to a 'Pool'. type Handle    = Node-data State     = Ready | Starting | forall a. Started ThreadId (TMVar a)+data State     = Ready | Starting | Started ThreadId SomeTMVar data Status    = Pending | Completed deriving (Eq, Show) type TaskGraph = Gr (TVar State) Status @@ -163,14 +163,20 @@     forM_ (labNodes g) $ \(_h, st) -> do         x <- readTVar st         case x of-            Started _tid v -> waitTMVar v+            Started _tid v -> waitSomeTMVar v             _ -> retry +data SomeTMVar where+    SomeTMVar :: forall a. TMVar a -> SomeTMVar++waitSomeTMVar :: SomeTMVar -> STM ()+waitSomeTMVar (SomeTMVar mv) = waitTMVar mv+ data TaskGroup = TaskGroup     { pool    :: Pool     , avail   :: TVar Int       -- ^ The number of available execution slots in the pool.-    , pending :: forall a. TVar (IntMap (IO ThreadId, TMVar a))+    , pending :: TVar (IntMap (IO ThreadId, SomeTMVar))       -- ^ Nodes in the task graph that are waiting to start.     } @@ -243,7 +249,7 @@             doFork $ try (restore (action `finally` cleanup h))                 >>= atomically . putTMVar var -    modifyTVar (pending p) (IntMap.insert h (start, var))+    modifyTVar (pending p) (IntMap.insert h (start, SomeTMVar var))     tv <- newTVar Ready     modifyTVar (tasks (pool p)) (insNode (h, tv)) @@ -253,6 +259,13 @@         modifyTVar (avail p) succ         cleanupTask (pool p) h +-- | Like 'asyncUsing' but waits until there are free slots in the TaskGroup+asyncUsingLazy :: TaskGroup -> (IO () -> IO ThreadId) -> IO a -> STM (Async a)+asyncUsingLazy p doFork action = do+    availSlots <- readTVar (avail p)+    check (availSlots > 0)+    asyncUsing p doFork action+ -- | Return the next available thread identifier from the pool.  These are --   monotonically increasing integers. nextIdent :: Pool -> STM Int@@ -710,10 +723,18 @@ -- exception handler. {-# INLINE rawForkIO #-} rawForkIO :: IO () -> IO ThreadId+#if MIN_VERSION_base(4,17,0)+rawForkIO (IO action) = IO $ \ s ->+#else rawForkIO action = IO $ \ s ->+#endif    case (fork# action s) of (# s1, tid #) -> (# s1, ThreadId tid #)  {-# INLINE rawForkOn #-} rawForkOn :: Int -> IO () -> IO ThreadId+#if MIN_VERSION_base(4,17,0)+rawForkOn (I# cpu) (IO action) = IO $ \ s ->+#else rawForkOn (I# cpu) action = IO $ \ s ->+#endif    case (forkOn# cpu action s) of (# s1, tid #) -> (# s1, ThreadId tid #)
Control/Concurrent/Async/Pool/Internal.hs view
@@ -10,7 +10,7 @@ import qualified Control.Concurrent.Async as Async (withAsync) import           Control.Concurrent.Async.Pool.Async import           Control.Concurrent.STM-import           Control.Exception (SomeException, throwIO, finally)+import           Control.Exception (SomeException, throwIO, finally, bracket_) import           Control.Monad hiding (forM, forM_) import           Control.Monad.Base import           Control.Monad.IO.Class (MonadIO(..))@@ -23,11 +23,10 @@ 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, TMVar a))+getReadyNodes :: TaskGroup -> TaskGraph -> STM (IntMap (IO ThreadId, SomeTMVar)) getReadyNodes p g = do     availSlots <- readTVar (avail p)     check (availSlots > 0)@@ -50,7 +49,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, TMVar a))]+getReadyTasks :: TaskGroup -> STM [(TVar State, (IO ThreadId, SomeTMVar))] getReadyTasks p = do     g <- readTVar (tasks (pool p))     map (first (getTaskVar g)) . IntMap.toList <$> getReadyNodes p g@@ -76,8 +75,7 @@ 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)+    return $ TaskGroup p c m  -- | Execute tasks in a given task group.  The number of slots determines how --   many threads may execute concurrently.@@ -101,7 +99,9 @@ withTaskGroupIn p n f = createTaskGroup p n >>= \g ->     Async.withAsync (runTaskGroup g) $ const $ f g `finally` cancelAll g --- | Create both a pool, and a task group with a given number of execution slots.+-- | Create both a pool, and a task group with a given number of execution slots,+--   but with a bounded lifetime. Once the given function exits, all tasks (that +--   are still running) in the TaskGroup will be cancelled. withTaskGroup :: Int -> (TaskGroup -> IO b) -> IO b withTaskGroup n f = createPool >>= \p -> withTaskGroupIn p n f @@ -158,6 +158,9 @@ asyncAfter :: TaskGroup -> Async b -> IO a -> IO (Async a) asyncAfter p parent = asyncAfterAll p [taskHandle parent] +extraWorkerWhileBlocked :: TaskGroup -> IO a -> IO a+extraWorkerWhileBlocked p = bracket_ (atomically $ modifyTVar' (avail p) (+ 1)) (atomically $ modifyTVar' (avail p) ((-) 1))+ -- | Helper function used by several of the variants of 'mapTasks' below. mapTasksWorker :: Traversable t                => TaskGroup@@ -166,8 +169,8 @@                -> (Async a -> IO b)                -> IO (t c) mapTasksWorker p fs f g = do-    hs <- forM fs $ atomically . asyncUsing p rawForkIO-    f $ forM hs g+    hs <- forM fs $ atomically . asyncUsingLazy p rawForkIO+    extraWorkerWhileBlocked p $ f $ forM hs g  -- | Execute a group of tasks within the given task group, returning the --   results in order.  The order of execution is random, but the results are
async-pool.cabal view
@@ -1,5 +1,5 @@ Name:           async-pool-Version:        0.9.1+Version:        0.9.2 Synopsis:       A modified version of async that supports worker groups and many-to-many task dependencies License-file:   LICENSE License:        MIT
test/main.hs view
@@ -191,3 +191,7 @@           end <- getCurrentTime           let diff = diffUTCTime end start           diff < 1.2 `shouldBe` True++      it "nested mapTasks work" $ withTaskGroup 1 $ \p -> do+          mapTasks p ([mapTasks p [pure ()]])+          True `shouldBe` True