packages feed

monadIO (empty) → 0.9.1.0

raw patch · 7 files changed

+668/−0 lines, 7 filesdep +basedep +mtldep +stmsetup-changed

Dependencies added: base, mtl, stm

Files

+ LICENSE view
@@ -0,0 +1,26 @@+Copyright (c) 2008-2010, Galois, Inc.++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:+1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.+2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO+EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,5 @@+module Main where++import Distribution.Simple++main = defaultMain
+ monadIO.cabal view
@@ -0,0 +1,25 @@+name:               monadIO+version:            0.9.1.0+synopsis:           Overloading of concurrency variables+description:        MonadIO provides for many IO operations to be+                    overloaded over other IO-like monads.+category:           Concurrency+license:            BSD3+license-file:       LICENSE+author:             John Launchbury+maintainer:         John Launchbury+Copyright:          (c) 2008-2010, Galois, Inc.+cabal-version:      >= 1.2.0+build-type:         Simple+++Library+  Build-Depends:    base >= 4.2.0.0 && <= 4.3,+                    stm  >= 2.1.2.0 && <= 2.2,+                    mtl  >= 1.1.0.0 && <= 1.2+  Exposed-modules:  Control.Concurrent.MonadIO+                    Control.Concurrent.STM.MonadIO+                    Control.Concurrent.StdInOut+                    Data.IORef.MonadIO+  Hs-Source-Dirs:   src+  Ghc-Options:      -Wall
+ src/Control/Concurrent/MonadIO.hs view
@@ -0,0 +1,197 @@+--------------------------------------------------------------------------+{- |+Module      : Control.Concurrent.MonadIO+Copyright   : (c) 2010 Galois, Inc.+License     : BSD-style (see the file libraries/base/LICENSE)++Maintainer  : John Launchbury, john@galois.com+Stability   : experimental+Portability : concurrency++Overloads the standard operations on MVars, Chans, and threads,+as defined in Control.Concurrent. This module is name-for-name+swappable with Control.Concurrent unless ghc-specific +operations like 'mergeIO' or 'threadWaitRead' are used.++The standard operations on 'MVar' and 'Chan' (such as+'newEmptyMVar', or 'putChan') are overloaded over the+'MonadIO' class. A monad @m@ is declared an instance of+'MonadIO' by defining a function++> liftIO :: IO a -> m a++The explicit concurrency operations over threads are+available if a monad @m@ is declared an instance of the+'HasFork' class, by defining a function++> fork :: m () -> m ThreadId+++ * Example use.++Suppose you define a new monad (EIO say) which is like+'IO' except that it provides an environment too.+You will need to declare EIO and instance of the 'Monad' class. In +addition, you can declare it in the 'MonadIO' class. For example:++>  newtype EIO a = EIO {useEnv :: Env -> IO a}+>	+>  instance MonadIO EIO where+>    liftIO m = EIO $ (\_ -> m)++Now the standard operations on 'MVar' and 'Chan' (such as+'newEmptyMVar', or 'putChan' are immediately available as+EIO operations. To enable EIO to fork explicit threads, and to+access operations such as 'killThread' and 'threadDelay', use+the declaration++>  instance HasFork EIO where+>    fork em = EIO $ \e -> forkIO (em `useEnv` e)++++ * Notes.++The 'MVar' operations do not include: withMVar, modifyMVar, or +addMVarFinalizer. Consider using TMVars for these instead. In particular,+modifyMVar seems to promise atomicity, but it is NOT atomic. In+contrast TMVars can be used just like MVars, and they+will behave the way you expect (module Control.Concurrent.STM.MonadIO).++-}++++--------------------------------------------------------------------------+++{-# OPTIONS_GHC -fno-warn-unused-do-bind     #-}+++module Control.Concurrent.MonadIO (+    MonadIO(..)+    +  , C.MVar+  , newEmptyMVar+  , newMVar+  , takeMVar+  , putMVar+  , readMVar+  , swapMVar+  , tryTakeMVar+  , tryPutMVar+  , isEmptyMVar+  +  , C.Chan+  , newChan+  , writeChan+  , readChan+  , dupChan+  , unGetChan+  , isEmptyChan+  , getChanContents+  , writeList2Chan+  +  , HasFork(..)+  , C.ThreadId+  , forkIO+  , myThreadId+  , killThread+  , throwTo+  , yield+  , threadDelay+  +  )+  where++import Control.Monad.Trans(MonadIO(..))+import qualified Control.Concurrent as C+import qualified Control.Exception as E++++newEmptyMVar  :: MonadIO io => io (C.MVar a)+newEmptyMVar   = liftIO $ C.newEmptyMVar++newMVar       :: MonadIO io => a -> io (C.MVar a)+newMVar x      = liftIO $ C.newMVar x++takeMVar      :: MonadIO io => C.MVar a -> io a+takeMVar m     = liftIO $ C.takeMVar m++putMVar       :: MonadIO io => C.MVar a -> a -> io ()+putMVar m x    = liftIO $ C.putMVar m x++readMVar      :: MonadIO io => C.MVar a -> io a+readMVar m     = liftIO $ C.readMVar m++swapMVar      :: MonadIO io => C.MVar a -> a -> io a+swapMVar m x   = liftIO $ C.swapMVar m x ++tryTakeMVar   :: MonadIO io => C.MVar a -> io (Maybe a)+tryTakeMVar m  = liftIO $ C.tryTakeMVar m++tryPutMVar    :: MonadIO io => C.MVar a -> a -> io Bool+tryPutMVar m x = liftIO $ C.tryPutMVar m x++isEmptyMVar   :: MonadIO io => C.MVar a -> io Bool+isEmptyMVar m  = liftIO $ C.isEmptyMVar m++++-------------------------------------------------------------------------++newChan            :: MonadIO io => io (C.Chan a)+newChan             = liftIO $ C.newChan++writeChan          :: MonadIO io => C.Chan a -> a -> io ()+writeChan c x       = liftIO $ C.writeChan c x++readChan           :: MonadIO io => C.Chan a -> io a+readChan c          = liftIO $ C.readChan c++dupChan            :: MonadIO io => C.Chan a -> io (C.Chan a)+dupChan c           = liftIO $ C.dupChan c ++unGetChan          :: MonadIO io => C.Chan a -> a -> io ()+unGetChan c x       = liftIO $ unGetChan c x++isEmptyChan        :: MonadIO io => C.Chan a -> io Bool+isEmptyChan c       = liftIO $ C.isEmptyChan c++getChanContents    :: MonadIO io => C.Chan a -> io [a]+getChanContents c   = liftIO $ C.getChanContents c++writeList2Chan     :: MonadIO io => C.Chan a -> [a] -> io ()+writeList2Chan c xs = liftIO $ C.writeList2Chan c xs+++-------------------------------------------------------------------------++class MonadIO io => HasFork io where+  fork :: io () -> io C.ThreadId++instance HasFork IO where+  fork m = C.forkIO m++-- | Included to maintain name-for-name compatibility+--   with Control.Concurrent+forkIO :: IO () -> IO C.ThreadId+forkIO = C.forkIO++myThreadId   :: HasFork io => io C.ThreadId+myThreadId    = liftIO $ C.myThreadId++killThread   :: HasFork io => C.ThreadId -> io ()+killThread i  = liftIO $ C.killThread i++throwTo      :: (E.Exception e, HasFork io) => C.ThreadId -> e -> io ()+throwTo i e   = liftIO $ C.throwTo i e++yield        :: HasFork io => io ()+yield         = liftIO $ C.yield++threadDelay  :: HasFork io => Int -> io ()+threadDelay n = liftIO $ C.threadDelay n++
+ src/Control/Concurrent/STM/MonadIO.hs view
@@ -0,0 +1,229 @@+--------------------------------------------------------------------------+{- |+Module      : Control.Concurrent.STM.MonadIO+Copyright   : (c) 2010 Galois, Inc.+License     : BSD-style (see the file libraries/base/LICENSE)++Maintainer  : John Launchbury, john@galois.com+Stability   : experimental+Portability : concurrency, requires STM++Overloads the standard operations on TVars, and TMVars as defined+in Control.Concurrent.STM.++TVars and MVars are often thought of as variables to be +used in the STM monad. But in practice, they should be used+just as frequently (if not more so) in any IO-like monad, with STM +being used purely when a new atomic transaction is being defined.+Thus we reverse the naming convention, and use+the plain access names when in the IO-like monad, and use an explicit STM +suffix when using the variables tentatively within the STM monad itself.++TMVars are particularly valuable when used in an IO-like monad,+because operations like readTMVar and modifyTMvar+can guarantee the atomicity of the operation (unlike the corresponding+operations over MVars).++The standard operations on 'TVar' and 'TMVar' (such as+'writeTVar' or 'newEmptyTMVar') are overloaded over the+'MonadIO' class. A monad @m@ is declared an instance of+'MonadIO' by defining a function++> liftIO :: IO a -> m a++It also overloads the 'atomically' function, so that STM transactions+can be defined from within any MonadIO monad.++-}++{-# OPTIONS_GHC -fno-warn-unused-do-bind     #-}++++module Control.Concurrent.STM.MonadIO (++    STM+  , atomically+  , always+  , alwaysSucceeds+  , retry +  , orElse +  , check +  , catchSTM ++  , S.TVar+  , newTVar+  , readTVar+  , writeTVar+  , registerDelay+  , modifyTVar+  , modifyTVar_++  , newTVarSTM+  , readTVarSTM+  , writeTVarSTM+++  , S.TMVar+  , newTMVar+  , newEmptyTMVar+  , takeTMVar+  , putTMVar+  , readTMVar+  , swapTMVar+  , tryTakeTMVar+  , tryPutTMVar+  , isEmptyTMVar+  , modifyTMVar +  , modifyTMVar_ ++  , newTMVarSTM+  , newEmptyTMVarSTM+  , takeTMVarSTM+  , putTMVarSTM+  , readTMVarSTM+  , swapTMVarSTM+  , tryTakeTMVarSTM+  , tryPutTMVarSTM+  , isEmptyTMVarSTM+  )+  where++import Control.Monad.STM hiding (atomically)+import qualified Control.Concurrent.STM as S+import Control.Concurrent.MonadIO++import GHC.Conc (readTVarIO)+++-------------------------------------------------------------------------+-- | The atomically function allows STM to be called directly from any+-- monad which contains IO, i.e. is a member of MonadIO.++atomically :: MonadIO io => STM a -> io a+atomically m = liftIO $ S.atomically m++-------------------------------------------------------------------------+++type TVar = S.TVar++newTVar      :: MonadIO io => a -> io (TVar a)+newTVar x     = liftIO $ S.newTVarIO x++readTVar     :: MonadIO io => TVar a -> io a+readTVar t    = liftIO $ readTVarIO t++writeTVar    :: MonadIO io => TVar a -> a -> io ()+writeTVar t x = atomically $ writeTVarSTM t x++registerDelay :: MonadIO io => Int -> io (TVar Bool)+registerDelay n = liftIO $ S.registerDelay n++-- | 'modifyTVar' is an atomic update operation which provides both+-- the former value and the newly computed value as a result.++modifyTVar :: MonadIO io => TVar a -> (a -> a) -> io (a,a)+modifyTVar t f = atomically $ do+                    x <- readTVarSTM t+                    let y = f x+                    seq y $ writeTVarSTM t y+                    return (x,y)++modifyTVar_ :: MonadIO io => TVar a -> (a -> a) -> io ()+modifyTVar_ t f = atomically $ do+                    x <- readTVarSTM t+                    let y = f x+                    seq y $ writeTVarSTM t y++----------------------+++newTVarSTM :: a -> STM (TVar a)+newTVarSTM x = S.newTVar x++readTVarSTM :: TVar a -> STM a+readTVarSTM t = S.readTVar t++writeTVarSTM :: TVar a -> a -> STM ()+writeTVarSTM t x = S.writeTVar t x+++++-------------------------------------------------------------------------++type TMVar a = S.TMVar a++newTMVar :: MonadIO io => a -> io (TMVar a)+newTMVar x = liftIO $ S.newTMVarIO x++newEmptyTMVar :: MonadIO io => io (TMVar a)+newEmptyTMVar = liftIO $ S.newEmptyTMVarIO++takeTMVar :: MonadIO io => TMVar a -> io a+takeTMVar t = atomically $ takeTMVarSTM t++putTMVar :: MonadIO io => TMVar a -> a -> io ()+putTMVar t x  = atomically $ putTMVarSTM t x++readTMVar :: MonadIO io => TMVar a -> io a+readTMVar t = atomically $ readTMVarSTM t++swapTMVar :: MonadIO io => TMVar a -> a -> io a+swapTMVar t x = atomically $ swapTMVarSTM t x++tryTakeTMVar :: MonadIO io => TMVar a -> io (Maybe a)+tryTakeTMVar t = atomically $ tryTakeTMVarSTM t++tryPutTMVar :: MonadIO io => TMVar a -> a -> io Bool+tryPutTMVar t x = atomically $ tryPutTMVarSTM t x++isEmptyTMVar :: MonadIO io => TMVar a -> io Bool+isEmptyTMVar t = atomically $ isEmptyTMVarSTM t++-- modifyTMVar is an atomic update operation which provides both+-- the former value and the newly computed value as a result.++modifyTMVar :: MonadIO io => TMVar a -> (a -> a) -> io (a,a)+modifyTMVar t f = atomically $ do+                    x <- takeTMVarSTM t+                    let y = f x+                    seq y $ putTMVarSTM t y+                    return (x,y)++modifyTMVar_ :: MonadIO io => TMVar a -> (a -> a) -> io ()+modifyTMVar_ t f = atomically $ do+                    x <- takeTMVarSTM t+                    let y = f x+                    seq y $ putTMVarSTM t y++----------------------++newTMVarSTM :: a -> STM (TMVar a)+newTMVarSTM = S.newTMVar++newEmptyTMVarSTM :: STM (TMVar a)+newEmptyTMVarSTM = S.newEmptyTMVar++takeTMVarSTM :: TMVar a -> STM a+takeTMVarSTM = S.takeTMVar++putTMVarSTM :: TMVar a -> a -> STM ()+putTMVarSTM = S.putTMVar++readTMVarSTM :: TMVar a -> STM a+readTMVarSTM = S.readTMVar++swapTMVarSTM :: TMVar a -> a -> STM a+swapTMVarSTM = S.swapTMVar++tryTakeTMVarSTM :: TMVar a -> STM (Maybe a)+tryTakeTMVarSTM = S.tryTakeTMVar++tryPutTMVarSTM :: TMVar a -> a -> STM Bool+tryPutTMVarSTM = S.tryPutTMVar++isEmptyTMVarSTM :: TMVar a -> STM Bool+isEmptyTMVarSTM = S.isEmptyTMVar+
+ src/Control/Concurrent/StdInOut.hs view
@@ -0,0 +1,126 @@+--------------------------------------------------------------------------+{- |+Module      : Control.Concurrent.StdInOut+Copyright   : (c) 2010 Galois, Inc.+License     : BSD-style (see the file libraries/base/LICENSE)++Maintainer  : John Launchbury, john@galois.com+Stability   : experimental+Portability : concurrency++A low-tech concurrent interface to the console. When multiple threads+want input, they send messages to the console with the format++>  <thread-id>:request++The user supplies input to any requesting thread in a similar way:++>  <thread-id>:response++At any time, the user can enter @!!@ to obtain a listing of all the+active prompts. Any input not of either of these forms is discarded.++> example :: IO ()+> example = setupStdinout processes+> +> processes :: IO ()+> processes = do+>     forkIO $ (prompt "Enter something" >> return ())+>     forkIO $ (prompt "Something else" >> return ())+>     prompt "quit"      -- When the main thread dies,+>     return ()          -- the whole interaction ends++-}++--------------------------------------------------------------------------++{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}++module Control.Concurrent.StdInOut+  ( setupStdInOut  -- :: IO a -> IO a+  , prompt         -- :: String -> IO String+  , putStrLine     -- :: String -> IO ()+  ) where+++import System.IO(hSetBuffering,BufferMode(..),stdout)+import System.IO.Unsafe(unsafePerformIO) -- for a global variable+import Control.Monad(forever,join)+import Control.Exception(finally)++import Control.Concurrent.MonadIO++import Control.Concurrent.STM.MonadIO+++flag :: String+flag = "!!"                          -- User input to list active prompts+timeDelay :: Int+timeDelay = 100000                   -- 1/10th sec+++-- 'std' is a concurrent variable that holds the current input line for+-- other processes to consider whether they want to grab it. The+-- process 'forever inputScan' maintains it.++type Stdinout = TMVar String++{-# NOINLINE std #-}+std :: Stdinout+std = unsafePerformIO $ newEmptyTMVar  -- global location for input strings++-- | 'setupStdInOut' establishes the context for 'prompt', by running+--   a daemon while its argument is executing. The daemon is terminated+--   once the argument to 'setupStdInOut' finishes.++setupStdInOut :: IO a -> IO a+setupStdInOut procs = do+    hSetBuffering stdout LineBuffering   -- prevents character interleaving+    tid <- fork $ forever inputScan      -- daemon putting stdin into std+    procs `finally` killThread tid++inputScan :: IO (Maybe String)+inputScan = do+    str <- getLine                       -- get next input line from stdin+    putTMVar std str                     -- make the input available to others+    threadDelay timeDelay                -- give others time to grab it+    tryTakeTMVar std                     -- clear the input if still present+++-- | 'prompt' is the main user level function of the module. The function+-- prints its argument on stdout, prefixed by its process number. The user +-- similarly selects the recipient by prefixing the process number,+-- e.g. "23:". Active prompts will reprompt when !! is entered. ++prompt :: HasFork io => String -> io String+prompt text = do+    name <- myThreadNumber+    putStrLine (name ++ text)+    (join . atomically) $ do+       str <- takeTMVarSTM std           -- Grab the input string to examine+       case match name str of+         Just inp -> return (return inp) -- Exit with the string contents+         Nothing -> do+            check (str==flag)            -- Test whether to reprint the prompt+            putTMVarSTM std flag         -- Replace the flag for others to see+            return $ do+               threadDelay (2*timeDelay) -- Wait for the flag to flush+               prompt text               -- Recurse, to reprint the prompt++myThreadNumber :: HasFork io => io String+myThreadNumber = do+    tid <- myThreadId+    return $ drop (length "ThreadId ") (show tid ++ ":")++match :: String -> String -> Maybe String+match n s = if n == take (length n) s then+              Just (drop (length n) s)+            else+              Nothing++-- | 'putStrLine' sends output to stdout, ensuring that lines are whole+-- and uninterrupted (including the final newline).++putStrLine :: MonadIO io => String -> io ()+putStrLine s = liftIO $ putStr (s ++ "\n")  +
+ src/Data/IORef/MonadIO.hs view
@@ -0,0 +1,60 @@+--------------------------------------------------------------------------+{- |+Module      : Data.IORef.MonadIO+Copyright   : (c) 2010 Galois, Inc.+License     : BSD-style (see the file libraries/base/LICENSE)++Maintainer  : John Launchbury, john@galois.com+Stability   : experimental+Portability : IO++Overloads the standard operations on IORefs,+as defined in Data.IORef. This module is name-for-name+swappable with Data.IORef unless ghc-specific +operations like weak pointers are used.++The standard operations on 'IORef' (such as+'newIORef', or 'modifyIORef') are overloaded over the+'MonadIO' class. A monad @m@ is declared an instance of+'MonadIO' by defining a function++> liftIO :: IO a -> m a++-}++--------------------------------------------------------------------------++++module Data.IORef.MonadIO (+    MonadIO(..)+    +  , R.IORef+  , newIORef+  , readIORef+  , writeIORef+  , modifyIORef+  , atomicModifyIORef+  +  )+  where++import Control.Monad.Trans(MonadIO(..))+import qualified Data.IORef as R+++newIORef :: MonadIO io => a -> io (R.IORef a)+newIORef x = liftIO $ R.newIORef x++readIORef :: MonadIO io => R.IORef a -> io a+readIORef r = liftIO $ R.readIORef r++writeIORef :: MonadIO io => R.IORef a -> a -> io ()+writeIORef r x = liftIO $ R.writeIORef r x++modifyIORef :: MonadIO io => R.IORef a -> (a -> a) -> io ()+modifyIORef r f = liftIO $ R.modifyIORef r f++atomicModifyIORef :: MonadIO io => R.IORef a -> (a -> (a, b)) -> io b+atomicModifyIORef r f = liftIO $ R.atomicModifyIORef r f+