--------------------------------------------------------------------------
{- |
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
  , 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

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