packages feed

async 2.2.2 → 2.2.3

raw patch · 3 files changed

+160/−78 lines, 3 filesdep ~basePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base

API changes (from Hackage documentation)

Files

Control/Concurrent/Async.hs view
@@ -24,65 +24,123 @@ -- "Control.Concurrent".  The main additional functionality it -- provides is the ability to wait for the return value of a thread, -- but the interface also provides some additional safety and--- robustness over using threads and @MVar@ directly.+-- robustness over using 'forkIO' threads and @MVar@ directly. --+-- == High-level API+--+-- @async@'s high-level API spawns /lexically scoped/ threads,+-- ensuring the following key poperties that make it safer to use+-- than using plain 'forkIO':+--+-- 1. No exception is swallowed (waiting for results propagates exceptions).+-- 2. No thread is leaked (left running unintentionally).+--+-- (This is done using the 'Control.Exception.bracket' pattern to work in presence+-- of synchornous and asynchronous exceptions.)+--+-- __Most practical/production code should only use the high-level API__.+-- -- The basic type is @'Async' a@, which represents an asynchronous -- @IO@ action that will return a value of type @a@, or die with an--- exception.  An @Async@ corresponds to a thread, and its 'ThreadId'--- can be obtained with 'asyncThreadId', although that should rarely--- be necessary.+-- exception.  An 'Async' is a wrapper around a low-level 'forkIO' thread. --+-- The fundamental function to spawn threads with the high-level API is+-- 'withAsync'.+-- -- For example, to fetch two web pages at the same time, we could do -- this (assuming a suitable @getURL@ function): ----- >    do a1 <- async (getURL url1)--- >       a2 <- async (getURL url2)--- >       page1 <- wait a1--- >       page2 <- wait a2--- >       ...+-- > withAsync (getURL url1) $ \a1 -> do+-- >   withAsync (getURL url2) $ \a2 -> do+-- >     page1 <- wait a1+-- >     page2 <- wait a2+-- >     ... ----- where 'async' starts the operation in a separate thread, and--- 'wait' waits for and returns the result.  If the operation--- throws an exception, then that exception is re-thrown by--- 'wait'.  This is one of the ways in which this library--- provides some additional safety: it is harder to accidentally--- forget about exceptions thrown in child threads.+-- where 'withAsync' starts the operation in a separate thread, and+-- 'wait' waits for and returns the result. ----- A slight improvement over the previous example is this:+-- * If the operation throws an exception, then that exception is re-thrown+--   by 'wait'. This ensures property (1): No exception is swallowed.+-- * If an exception bubbles up through a 'withAsync', then the 'Async'+--   it spawned is 'cancel'ed. This ensures property (2): No thread is leaked. ----- >       withAsync (getURL url1) $ \a1 -> do--- >       withAsync (getURL url2) $ \a2 -> do--- >       page1 <- wait a1--- >       page2 <- wait a2--- >       ...+-- Often we do not care to work manually with 'Async' handles like+-- @a1@ and @a2@. Instead, we want to express high-level objectives like+-- performing two or more tasks concurrently, and waiting for one or all+-- of them to finish. --+-- For example, the pattern of performing two IO actions concurrently and+-- waiting for both their results is packaged up in a combinator 'concurrently',+-- so we can further shorten the above example to:+--+-- > (page1, page2) <- concurrently (getURL url1) (getURL url2)+-- > ...+--+-- The section __/High-level utilities/__ covers the most+-- common high-level objectives, including:+--+-- * Waiting for 2 results ('concurrently').+-- * Waiting for many results ('mapConcurrently' / 'forConcurrently').+-- * Waiting for the first of 2 results ('race').+-- * Waiting for arbitrary nestings of "all of /N/" and "the first of /N/"+--   results with the 'Concurrently' newtype and its 'Applicative' and+--   'Alternative' instances.+--+-- Click here to scroll to that section:+-- "Control.Concurrent.Async#high-level-utilities".+--+-- == Low-level API+--+-- Some use cases require parallelism that is not lexically scoped.+--+-- For those, the low-level function 'async' can be used as a direct+-- equivalent of 'forkIO':+--+-- > -- Do NOT use this code in production, it has a flaw (explained below).+-- > do+-- >   a1 <- async (getURL url1)+-- >   a2 <- async (getURL url2)+-- >   page1 <- wait a1+-- >   page2 <- wait a2+-- >   ...+--+-- In contrast to 'withAsync', this code has a problem.+--+-- It still fulfills property (1) in that an exception arising from+-- @getUrl@ will be re-thrown by 'wait', but it does not fulfill+-- property (2).+-- Consider the case when the first 'wait' throws an exception; then the+-- second 'wait' will not happen, and the second 'async' may be left+-- running in the background, possibly indefinitely.+-- -- 'withAsync' is like 'async', except that the 'Async' is -- automatically killed (using 'uninterruptibleCancel') if the--- enclosing IO operation returns before it has completed.  Consider--- the case when the first 'wait' throws an exception; then the second--- 'Async' will be automatically killed rather than being left to run--- in the background, possibly indefinitely.  This is the second way--- that the library provides additional safety: using 'withAsync'--- means we can avoid accidentally leaving threads running.+-- enclosing IO operation returns before it has completed. -- Furthermore, 'withAsync' allows a tree of threads to be built, such -- that children are automatically killed if their parents die for any -- reason. ----- The pattern of performing two IO actions concurrently and waiting--- for their results is packaged up in a combinator 'concurrently', so--- we can further shorten the above example to:+-- If you need to use the low-level API, ensure that you gurantee+-- property (2) by other means, such as 'link'ing asyncs that need+-- to die together, and protecting against asynchronous exceptions+-- using 'Control.Exception.bracket', 'Control.Exception.mask',+-- or other functions from "Control.Exception". ----- >       (page1, page2) <- concurrently (getURL url1) (getURL url2)--- >       ...+-- == Miscellaneous -- -- The 'Functor' instance can be used to change the result of an -- 'Async'.  For example: ----- > ghci> a <- async (return 3)--- > ghci> wait a--- > 3--- > ghci> wait (fmap (+1) a)+-- > ghci> withAsync (return 3) (\a -> wait (fmap (+1) a)) -- > 4+--+-- === Resource exhaustion+--+-- As with all concurrent programming, keep in mind that while+-- Haskell's cooperative ("green") multithreading carries low overhead,+-- spawning too many of them at the same time may lead to resource exhaustion+-- (of memory, file descriptors, or other limited resources), given that the+-- actions running in the threads consume these resources.  ----------------------------------------------------------------------------- @@ -90,9 +148,9 @@      -- * Asynchronous actions     Async,-    -- ** Spawning-    async, asyncBound, asyncOn, asyncWithUnmask, asyncOnWithUnmask, +    -- * High-level API+     -- ** Spawning with automatic 'cancel'ation     withAsync, withAsyncBound, withAsyncOn, withAsyncWithUnmask,     withAsyncOnWithUnmask,@@ -101,33 +159,40 @@     wait, poll, waitCatch, asyncThreadId,     cancel, uninterruptibleCancel, cancelWith, AsyncCancelled(..), -    -- ** STM operations+    -- ** #high-level-utilities# High-level utilities+    race, race_,+    concurrently, concurrently_,+    mapConcurrently, forConcurrently,+    mapConcurrently_, forConcurrently_,+    replicateConcurrently, replicateConcurrently_,+    Concurrently(..),+    compareAsyncs,++    -- ** Specialised operations++    -- *** STM operations     waitSTM, pollSTM, waitCatchSTM, -    -- ** Waiting for multiple 'Async's+    -- *** Waiting for multiple 'Async's     waitAny, waitAnyCatch, waitAnyCancel, waitAnyCatchCancel,     waitEither, waitEitherCatch, waitEitherCancel, waitEitherCatchCancel,     waitEither_,     waitBoth, -    -- ** Waiting for multiple 'Async's in STM+    -- *** Waiting for multiple 'Async's in STM     waitAnySTM, waitAnyCatchSTM,     waitEitherSTM, waitEitherCatchSTM,     waitEitherSTM_,     waitBothSTM, +    -- * Low-level API++    -- ** Spawning (low-level API)+    async, asyncBound, asyncOn, asyncWithUnmask, asyncOnWithUnmask,+     -- ** Linking     link, linkOnly, link2, link2Only, ExceptionInLinkedThread(..), -    -- * Convenient utilities-    race, race_,-    concurrently, concurrently_,-    mapConcurrently, forConcurrently,-    mapConcurrently_, forConcurrently_,-    replicateConcurrently, replicateConcurrently_,-    Concurrently(..),-    compareAsyncs,-   ) where  import Control.Concurrent.STM@@ -185,11 +250,16 @@ instance Functor Async where   fmap f (Async a w) = Async a (fmap (fmap f) w) --- | Compare two Asyncs that may have different types by their ThreadId.+-- | Compare two Asyncs that may have different types by their 'ThreadId'. compareAsyncs :: Async a -> Async b -> Ordering compareAsyncs (Async t1 _) (Async t2 _) = compare t1 t2  -- | Spawn an asynchronous action in a separate thread.+--+-- Like for 'forkIO', the action may be left running unintentinally+-- (see module-level documentation for details).+--+-- __Use 'withAsync' style functions wherever you can instead!__ async :: IO a -> IO (Async a) async = inline asyncUsing rawForkIO @@ -516,7 +586,10 @@ -- {-# INLINE waitBoth #-} waitBoth :: Async a -> Async b -> IO (a,b)-waitBoth left right = atomically (waitBothSTM left right)+waitBoth left right = tryAgain $ atomically (waitBothSTM left right)+  where+    -- See: https://github.com/simonmar/async/issues/14+    tryAgain f = f `catch` \BlockedIndefinitelyOnSTM -> f  -- | A version of 'waitBoth' that can be used inside an STM transaction. --@@ -647,6 +720,11 @@ -- >   waitBoth a b concurrently :: IO a -> IO b -> IO (a,b) +-- | 'concurrently', but ignore the result values+--+-- @since 2.1.1+concurrently_ :: IO a -> IO b -> IO ()+ #define USE_ASYNC_VERSIONS 0  #if USE_ASYNC_VERSIONS@@ -656,16 +734,15 @@   withAsync right $ \b ->   waitEither a b -race_ left right =-  withAsync left $ \a ->-  withAsync right $ \b ->-  waitEither_ a b+race_ left right = void $ race left right  concurrently left right =   withAsync left $ \a ->   withAsync right $ \b ->   waitBoth a b +concurrently_ left right = void $ concurrently left right+ #else  -- MVar versions of race/concurrently@@ -744,9 +821,19 @@         stop         return r +concurrently_ left right = concurrently' left right (collect 0)+  where+    collect 2 _ = return ()+    collect i m = do+        e <- m+        case e of+            Left ex -> throwIO ex+            Right _ -> collect (i + 1 :: Int) m++ #endif --- | maps an @IO@-performing function over any @Traversable@ data+-- | Maps an 'IO'-performing function over any 'Traversable' data -- type, performing all the @IO@ actions concurrently, and returning -- the original data structure with the arguments replaced by the -- results.@@ -758,6 +845,10 @@ -- -- > pages <- mapConcurrently getURL ["url1", "url2", "url3"] --+-- Take into account that @async@ will try to immediately spawn a thread+-- for each element of the @Traversable@, so running this on large+-- inputs without care may lead to resource exhaustion (of memory,+-- file descriptors, or other limited resources). mapConcurrently :: Traversable t => (a -> IO b) -> t a -> IO (t b) mapConcurrently f = runConcurrently . traverse (Concurrently . f) @@ -769,28 +860,15 @@ forConcurrently :: Traversable t => t a -> (a -> IO b) -> IO (t b) forConcurrently = flip mapConcurrently --- | `mapConcurrently_` is `mapConcurrently` with the return value discarded,--- just like @mapM_@.+-- | `mapConcurrently_` is `mapConcurrently` with the return value discarded;+-- a concurrent equivalent of 'mapM_'. mapConcurrently_ :: F.Foldable f => (a -> IO b) -> f a -> IO () mapConcurrently_ f = runConcurrently . F.foldMap (Concurrently . void . f) --- | `forConcurrently_` is `forConcurrently` with the return value discarded,--- just like @forM_@.+-- | `forConcurrently_` is `forConcurrently` with the return value discarded;+-- a concurrent equivalent of 'forM_'. forConcurrently_ :: F.Foldable f => f a -> (a -> IO b) -> IO () forConcurrently_ = flip mapConcurrently_---- | 'concurrently', but ignore the result values------ @since 2.1.1-concurrently_ :: IO a -> IO b -> IO ()-concurrently_ left right = concurrently' left right (collect 0)-  where-    collect 2 _ = return ()-    collect i m = do-        e <- m-        case e of-            Left ex -> throwIO ex-            Right _ -> collect (i + 1 :: Int) m  -- | Perform the action in the given number of threads. --
async.cabal view
@@ -1,5 +1,5 @@ name:                async-version:             2.2.2+version:             2.2.3 -- don't forget to update ./changelog.md! synopsis:            Run IO operations asynchronously and wait for their results @@ -34,7 +34,7 @@ cabal-version:       >=1.10 homepage:            https://github.com/simonmar/async bug-reports:         https://github.com/simonmar/async/issues-tested-with:         GHC==8.6.5, GHC==8.4.4, GHC==8.2.2, GHC==8.0.2, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2, GHC==7.2.2, GHC==7.0.4+tested-with:         GHC==8.10.1, GHC==8.8.3, GHC==8.6.5, GHC==8.4.4, GHC==8.2.2, GHC==8.0.2, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2, GHC==7.2.2, GHC==7.0.4  extra-source-files:     changelog.md@@ -50,14 +50,14 @@     if impl(ghc>=7.1)         other-extensions: Trustworthy     exposed-modules:     Control.Concurrent.Async-    build-depends:       base >= 4.3 && < 4.14, hashable >= 1.1.2.0 && < 1.4, stm >= 2.2 && < 2.6+    build-depends:       base >= 4.3 && < 4.16, hashable >= 1.1.2.0 && < 1.4, stm >= 2.2 && < 2.6  test-suite test-async     default-language: Haskell2010     type:       exitcode-stdio-1.0     hs-source-dirs: test     main-is:    test-async.hs-    build-depends: base >= 4.3 && < 4.14,+    build-depends: base >= 4.3 && < 4.15,                    async,                    stm,                    test-framework,
changelog.md view
@@ -1,3 +1,7 @@+## Changes in 2.2.3:++ - Documentation fixes+ ## Changes in 2.2.2:   - Builds with GHC 8.6.x