typed-process 0.2.2.0 → 0.2.3.0
raw patch · 4 files changed
+87/−20 lines, 4 filesdep ~asyncdep ~basedep ~bytestring
Dependency ranges changed: async, base, bytestring
Files
- ChangeLog.md +4/−0
- README.md +6/−0
- src/System/Process/Typed.hs +49/−13
- typed-process.cabal +28/−7
ChangeLog.md view
@@ -1,3 +1,7 @@+## 0.2.3.0++* Add support for the single-threaded runtime via polling+ ## 0.2.2.0 * Add inherit versions of setter functions
README.md view
@@ -19,6 +19,12 @@ 5. Providing a more composable API, designed to be easy to use for both simple and complex use cases +__NOTE__ It's highly recommended that you compile any program using this+library with the multi-threaded runtime, usually by adding `ghc-options:+-threaded` to your executable stanza in your cabal or `package.yaml` file. The+single-threaded runtime necessitates some inefficient polling to be used under+the surface.+ ## Synopsis ```haskell
src/System/Process/Typed.hs view
@@ -6,15 +6,7 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE ScopedTypeVariables #-}-{-|-Please see the README.md file for examples of using this API.--__Applications using this library MUST use @-threaded@__ to link with the-threaded version of the RTS. 'spawnProcess' and the others spawn a Haskell-thread that blocks until the process exits. Without @-threaded@ this will block-/all/ the Haskell threads in your program until the spawned process exits.---}+-- | Please see the README.md file for examples of using this API. module System.Process.Typed ( -- * Types ProcessConfig@@ -98,18 +90,21 @@ import qualified Data.ByteString as S import Data.ByteString.Lazy.Internal (defaultChunkSize)-import Control.Exception (assert, evaluate, throwIO, Exception, SomeException, finally, bracket, onException, catch)+import Control.Exception (assert, evaluate, throwIO, Exception, SomeException, finally, bracket, onException, catch, try) import Control.Monad (void) import Control.Monad.IO.Class import qualified System.Process as P import Data.Typeable (Typeable) import System.IO (Handle, hClose)+import System.IO.Error (isPermissionError)+import Control.Concurrent (threadDelay) import Control.Concurrent.Async (async, cancel, waitCatch) import Control.Concurrent.STM (newEmptyTMVarIO, atomically, putTMVar, TMVar, readTMVar, tryReadTMVar, STM, tryPutTMVar, throwSTM, catchSTM) import System.Exit (ExitCode (ExitSuccess)) import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy.Char8 as L8 import Data.String (IsString (fromString))+import GHC.RTS.Flags (getConcFlags, ctxtSwitchTime) #if MIN_VERSION_process(1, 4, 0) && !WINDOWS import System.Posix.Types (GroupID, UserID)@@ -624,7 +619,21 @@ pExitCode <- newEmptyTMVarIO waitingThread <- async $ do- ec <- P.waitForProcess pHandle+ ec <-+ if multiThreadedRuntime+ then P.waitForProcess pHandle+ else do+ switchTime <- (fromIntegral . (`div` 1000) . ctxtSwitchTime)+ <$> getConcFlags+ let minDelay = 1+ maxDelay = max minDelay switchTime+ loop delay = do+ threadDelay delay+ mec <- P.getProcessExitCode pHandle+ case mec of+ Nothing -> loop $ min maxDelay (delay * 2)+ Just ec -> pure ec+ loop minDelay atomically $ putTMVar pExitCode ec return ec @@ -645,14 +654,41 @@ -- Process didn't exit yet, let's terminate it and -- then call waitForProcess ourselves Left _ -> do- P.terminateProcess pHandle- ec <- P.waitForProcess pHandle+ eres <- try $ P.terminateProcess pHandle+ ec <-+ case eres of+ Left e+ -- On Windows, with the single-threaded runtime, it+ -- seems that if a process has already exited, the+ -- call to terminateProcess will fail with a+ -- permission denied error. To work around this, we+ -- catch this exception and then immediately+ -- waitForProcess. There's a chance that there may be+ -- other reasons for this permission error to appear,+ -- in which case this code may allow us to wait too+ -- long for a child process instead of erroring out.+ -- Recommendation: always use the multi-threaded+ -- runtime!+ | isPermissionError e && not multiThreadedRuntime && isWindows ->+ P.waitForProcess pHandle+ | otherwise -> throwIO e+ Right () -> P.waitForProcess pHandle success <- atomically $ tryPutTMVar pExitCode ec evaluate $ assert success () return Process {..} where pConfig = clearStreams pConfig'++foreign import ccall unsafe "rtsSupportsBoundThreads"+ multiThreadedRuntime :: Bool++isWindows :: Bool+#if WINDOWS+isWindows = True+#else+isWindows = False+#endif -- | Close a process and release any resources acquired. This will -- ensure 'P.terminateProcess' is called, wait for the process to
typed-process.cabal view
@@ -1,11 +1,13 @@--- This file has been generated from package.yaml by hpack version 0.21.2.+cabal-version: >= 1.10++-- This file has been generated from package.yaml by hpack version 0.29.0. -- -- see: https://github.com/sol/hpack ----- hash: 11068332a1f3de188a488a22fdb0b1bc49f70ccc1dd04c322123278d6d6124e3+-- hash: 8fd30ba42322fffadc01326bcc1370fa86c650057800ca91d5381d89e91df797 name: typed-process-version: 0.2.2.0+version: 0.2.3.0 synopsis: Run external processes, with strong typing of streams description: Please see the tutorial at <https://haskell-lang.org/library/typed-process> category: System@@ -16,8 +18,6 @@ license: MIT license-file: LICENSE build-type: Simple-cabal-version: >= 1.10- extra-source-files: ChangeLog.md README.md@@ -35,7 +35,7 @@ src build-depends: async- , base >=4.7 && <5+ , base >=4.8 && <5 , bytestring , process >=1.2 , stm@@ -55,7 +55,28 @@ ghc-options: -threaded -rtsopts -with-rtsopts=-N build-depends: async- , base >=4.7 && <5+ , base >=4.8 && <5+ , base64-bytestring+ , bytestring+ , hspec+ , process >=1.2+ , stm+ , temporary+ , transformers+ , typed-process+ default-language: Haskell2010++test-suite typed-process-test-single-threaded+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ other-modules:+ System.Process.TypedSpec+ Paths_typed_process+ hs-source-dirs:+ test+ build-depends:+ async+ , base >=4.8 && <5 , base64-bytestring , bytestring , hspec