packages feed

shelly-extra 0.2.3.1 → 0.3

raw patch · 2 files changed

+33/−37 lines, 2 filesdep +asyncdep +mtldep ~shelly

Dependencies added: async, mtl

Dependency ranges changed: shelly

Files

Shelly/Background.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE OverloadedStrings, ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, ExistentialQuantification #-} -- | A futures implementation that integrates with shelly --  -- > jobs 5 (\job -> background job (sleep 2) >> background job (sleep 1))@@ -8,14 +8,15 @@ -- Generally shell scripts contain a lot of quick commands, but when you have the occasional command that is noticeably long and independent of other commands, you can easily run it concurrently. module Shelly.Background (    -- * Running external commands asynchronously.-   jobs, background, getBgResult, BgResult+   jobs, background, killAllJobs ) where  import Shelly-import Control.Concurrent-import Control.Exception (finally, catch, throwIO, SomeException)-import Prelude hiding (catch) import qualified Control.Concurrent.MSemN as Sem+import Data.IORef+import Control.Concurrent.Async+import Control.Monad.Trans ( MonadIO )+import Control.Monad ( void )  -- | Create a 'BgJobManager' that has a 'limit' on the max number of background tasks. -- an invocation of jobs is independent of any others, and not tied to the Sh monad in any way.@@ -24,45 +25,38 @@ jobs limit action = do     unless (limit > 0) $ terror "expected limit to be > 0"     availableJobsSem <- liftIO $ Sem.new limit-    res <- action $ BgJobManager availableJobsSem+    res <- liftIO (newIORef []) >>= action . BgJobManager availableJobsSem     liftIO $ Sem.wait availableJobsSem limit     return res  -- | The manager tracks the number of jobs. Register your 'background' jobs with it.-newtype BgJobManager = BgJobManager (Sem.MSemN Int)---- | Type returned by tasks run asynchronously in the background.-newtype BgResult a = BgResult (MVar a)+data BgJobManager = BgJobManager (Sem.MSemN Int) (IORef [Async ()]) --- | Returns the promised result from a backgrounded task.  Blocks until--- the task completes.-getBgResult :: BgResult a -> Sh a-getBgResult (BgResult mvar) = liftIO $ takeMVar mvar+killAllJobs :: MonadIO m => BgJobManager -> m ()+killAllJobs man = getJobs man >>= mapM_ (liftIO . cancel)+  where+    getJobs :: MonadIO m => BgJobManager -> m [Async ()]+    getJobs (BgJobManager _ asyncs) = liftIO $ readIORef asyncs --- | Run the `Sh` task asynchronously in the background, returns--- the `BgResult a`, a promise immediately. Run "getBgResult" to wait for the result.+-- | Run the 'Sh' task asynchronously in the background,+-- immediately returns the 'Async' promise. -- The background task will inherit the current Sh context -- The 'BgJobManager' ensures the max jobs limit must be sufficient for the parent and all children.-background :: BgJobManager -> Sh a -> Sh (BgResult a)-background (BgJobManager manager) proc = do-  state <- get-  liftIO $ do+background :: BgJobManager -> Sh a -> Sh (Async a)+background (BgJobManager manager asyncs) proc = do     -- take up a spot     -- It is important to do this before forkIO:     -- It ensures that that jobs will block and the program won't exit before our jobs are done     -- On the other hand, a user might not expect 'jobs' to block-    Sem.wait manager 1-    mvar <- newEmptyMVar -- future result--    -- probably should use async package rather than manually handling exception stuff-    mainTid <- myThreadId-    _<- forkIO $ do-      result <--        finally (-            shelly (put state >> proc) `catch`-              (\(e::SomeException) -> throwTo mainTid e >> throwIO e)-          )-          (Sem.signal manager 1) -- open a spot back up-      putMVar mvar result-    return $ BgResult mvar-+    liftIO $ Sem.wait manager 1+    a <- asyncSh $ finally_sh proc (liftIO $ Sem.signal manager 1)+    liftIO $ do+        link a+        -- to make our types easier,+        -- we want [Async ()] for killall+        -- since killall doesn't care about return types+        -- perhaps there is a more elegant way to accomplish this?+        b <- async $ void $ wait a+        link2 a b+        atomicModifyIORef' asyncs (\as -> (b:as, ()))+        return a
shelly-extra.cabal view
@@ -1,6 +1,6 @@ Name:       shelly-extra -Version:     0.2.3.1+Version:     0.3 Synopsis:    shelly features that require extra dependencies  Description: A background job implementation for performing tasks in parallel.@@ -28,7 +28,9 @@ Library   Exposed-modules:     Shelly.Background -  Build-depends: base >= 4 && < 5, shelly >= 0.13, SafeSemaphore >= 0.7+  Build-depends: base >= 4 && < 5, shelly >= 1.4.3, SafeSemaphore >= 0.7+               , async+               , mtl                       >= 2    ghc-options: -Wall