{-# LANGUAGE CPP, UnicodeSyntax, NoImplicitPrelude, FlexibleContexts, RankNTypes #-}

#if __GLASGOW_HASKELL__ >= 702
{-# LANGUAGE Trustworthy #-}
#endif

{- |
Module      :  Control.Concurrent.Lifted
Copyright   :  Bas van Dijk
License     :  BSD-style

Maintainer  :  Bas van Dijk <v.dijk.bas@gmail.com>
Stability   :  experimental

This is a wrapped version of "Control.Concurrent" with types generalized
from 'IO' to all monads in either 'MonadBase' or 'MonadBaseControl'.
-}

module Control.Concurrent.Lifted
    ( -- * Concurrent Haskell
      ThreadId

      -- * Basic concurrency operations
    , myThreadId
    , fork
#if MIN_VERSION_base(4,4,0)
    , forkWithUnmask
#endif
#if MIN_VERSION_base(4,6,0)
    , forkFinally
#endif
    , killThread
    , throwTo

#if MIN_VERSION_base(4,4,0)
      -- ** Threads with affinity
    , forkOn
    , forkOnWithUnmask
    , getNumCapabilities
#if MIN_VERSION_base(4,6,0)
    , setNumCapabilities
#endif
    , threadCapability
#endif

      -- * Scheduling
    , yield

      -- ** Blocking
      -- ** Waiting
    , threadDelay
    , threadWaitRead
    , threadWaitWrite

      -- * Communication abstractions
    , module Control.Concurrent.MVar.Lifted
    , module Control.Concurrent.Chan.Lifted
#if !MIN_VERSION_base(4,6,0)
    , module Control.Concurrent.QSem.Lifted
    , module Control.Concurrent.QSemN.Lifted
    , module Control.Concurrent.SampleVar.Lifted
#endif

#if !MIN_VERSION_base(4,6,0)
      -- * Merging of streams
    , merge
    , nmerge
#endif

      -- * Bound Threads
    , C.rtsSupportsBoundThreads
    , forkOS
    , isCurrentThreadBound
    , runInBoundThread
    , runInUnboundThread

#if MIN_VERSION_base(4,6,0)
      -- * Weak references to ThreadIds
    , mkWeakThreadId
#endif
    ) where


--------------------------------------------------------------------------------
-- Imports
--------------------------------------------------------------------------------

-- from base:
import Data.Bool          ( Bool )
import Data.Int           ( Int )
import Data.Function      ( ($) )
import System.IO          ( IO )
import System.Posix.Types ( Fd )
#if MIN_VERSION_base(4,6,0)
import Control.Monad      ( (>>=) )
import Data.Either        ( Either )
import System.Mem.Weak    ( Weak )
#endif

import           Control.Concurrent ( ThreadId )
import qualified Control.Concurrent as C

-- from base-unicode-symbols:
import Data.Function.Unicode ( () )

-- from transformers-base:
import Control.Monad.Base ( MonadBase, liftBase )

-- from monad-control:
import Control.Monad.Trans.Control ( MonadBaseControl, liftBaseOp_, liftBaseDiscard )

#if MIN_VERSION_base(4,4,0)
import Control.Monad.Trans.Control ( liftBaseWith )
import Control.Monad               ( void )
#endif

-- from lifted-base (this package):
import Control.Concurrent.MVar.Lifted
import Control.Concurrent.Chan.Lifted
#if !MIN_VERSION_base(4,6,0)
import Control.Concurrent.QSem.Lifted
import Control.Concurrent.QSemN.Lifted
import Control.Concurrent.SampleVar.Lifted
#endif
import Control.Exception.Lifted ( throwTo
#if MIN_VERSION_base(4,6,0)
                                , SomeException, try, mask
#endif
                                )
#include "inlinable.h"


--------------------------------------------------------------------------------
-- Control.Concurrent
--------------------------------------------------------------------------------

-- | Generalized version of 'C.myThreadId'.
myThreadId  MonadBase IO m  m ThreadId
myThreadId = liftBase C.myThreadId
{-# INLINABLE myThreadId #-}

-- | Generalized version of 'C.forkIO'.
--
-- Note that, while the forked computation @m ()@ has access to the captured
-- state, all its side-effects in @m@ are discarded. It is run only for its
-- side-effects in 'IO'.
fork  MonadBaseControl IO m  m ()  m ThreadId
fork = liftBaseDiscard C.forkIO
{-# INLINABLE fork #-}

#if MIN_VERSION_base(4,4,0)
-- | Generalized version of 'C.forkIOWithUnmask'.
--
-- Note that, while the forked computation @m ()@ has access to the captured
-- state, all its side-effects in @m@ are discarded. It is run only for its
-- side-effects in 'IO'.
forkWithUnmask  MonadBaseControl IO m  (( a. m a  m a)  m ())  m ThreadId
forkWithUnmask f = liftBaseWith $ \runInIO 
                     C.forkIOWithUnmask $ \unmask 
                       void $ runInIO $ f $ liftBaseOp_ unmask
{-# INLINABLE forkWithUnmask #-}
#endif

#if MIN_VERSION_base(4,6,0)
-- | Generalized version of 'C.forkFinally'.
--
-- Note that in @forkFinally action and_then@, while the forked
-- @action@ and the @and_then@ function have access to the captured
-- state, all their side-effects in @m@ are discarded. They're run
-- only for their side-effects in 'IO'.
forkFinally  MonadBaseControl IO m
             m a  (Either SomeException a  m ())  m ThreadId
forkFinally action and_then =
    mask $ \restore ->
      fork $ try (restore action) >>= and_then
{-# INLINABLE forkFinally #-}
#endif

-- | Generalized version of 'C.killThread'.
killThread  MonadBase IO m  ThreadId  m ()
killThread = liftBase  C.killThread
{-# INLINABLE  killThread #-}

#if MIN_VERSION_base(4,4,0)
-- | Generalized version of 'C.forkOn'.
--
-- Note that, while the forked computation @m ()@ has access to the captured
-- state, all its side-effects in @m@ are discarded. It is run only for its
-- side-effects in 'IO'.
forkOn  MonadBaseControl IO m  Int  m ()  m ThreadId
forkOn = liftBaseDiscard  C.forkOn
{-# INLINABLE forkOn #-}

-- | Generalized version of 'C.forkOnWithUnmask'.
--
-- Note that, while the forked computation @m ()@ has access to the captured
-- state, all its side-effects in @m@ are discarded. It is run only for its
-- side-effects in 'IO'.
forkOnWithUnmask  MonadBaseControl IO m  Int  (( a. m a  m a)  m ())  m ThreadId
forkOnWithUnmask cap f = liftBaseWith $ \runInIO 
                           C.forkOnWithUnmask cap $ \unmask 
                             void $ runInIO $ f $ liftBaseOp_ unmask
{-# INLINABLE forkOnWithUnmask #-}

-- | Generalized version of 'C.getNumCapabilities'.
getNumCapabilities  MonadBase IO m  m Int
getNumCapabilities = liftBase C.getNumCapabilities
{-# INLINABLE getNumCapabilities #-}

#if MIN_VERSION_base(4,6,0)
-- | Generalized version of 'C.setNumCapabilities'.
setNumCapabilities  MonadBase IO m  Int  m ()
setNumCapabilities = liftBase  C.setNumCapabilities
{-# INLINABLE setNumCapabilities #-}
#endif

-- | Generalized version of 'C.threadCapability'.
threadCapability  MonadBase IO m  ThreadId  m (Int, Bool)
threadCapability = liftBase  C.threadCapability
{-# INLINABLE threadCapability #-}
#endif

-- | Generalized version of 'C.yield'.
yield  MonadBase IO m  m ()
yield = liftBase C.yield
{-# INLINABLE yield #-}

-- | Generalized version of 'C.threadDelay'.
threadDelay  MonadBase IO m  Int  m ()
threadDelay = liftBase   C.threadDelay
{-# INLINABLE threadDelay #-}

-- | Generalized version of 'C.threadWaitRead'.
threadWaitRead  MonadBase IO m  Fd  m ()
threadWaitRead = liftBase  C.threadWaitRead
{-# INLINABLE threadWaitRead #-}

-- | Generalized version of 'C.threadWaitWrite'.
threadWaitWrite  MonadBase IO m  Fd  m ()
threadWaitWrite = liftBase  C.threadWaitWrite
{-# INLINABLE threadWaitWrite #-}

#if !MIN_VERSION_base(4,6,0)
-- | Generalized version of 'C.mergeIO'.
merge  MonadBase IO m  [a]  [a]  m [a]
merge xs ys = liftBase $ C.mergeIO xs ys
{-# INLINABLE merge #-}

-- | Generalized version of 'C.nmergeIO'.
nmerge  MonadBase IO m  [[a]]  m [a]
nmerge = liftBase  C.nmergeIO
{-# INLINABLE nmerge #-}
#endif

-- | Generalized version of 'C.forkOS'.
--
-- Note that, while the forked computation @m ()@ has access to the captured
-- state, all its side-effects in @m@ are discarded. It is run only for its
-- side-effects in 'IO'.
forkOS  MonadBaseControl IO m  m ()  m ThreadId
forkOS = liftBaseDiscard C.forkOS
{-# INLINABLE forkOS #-}

-- | Generalized version of 'C.isCurrentThreadBound'.
isCurrentThreadBound  MonadBase IO m  m Bool
isCurrentThreadBound = liftBase C.isCurrentThreadBound
{-# INLINABLE isCurrentThreadBound #-}

-- | Generalized version of 'C.runInBoundThread'.
runInBoundThread  MonadBaseControl IO m  m a  m a
runInBoundThread = liftBaseOp_ C.runInBoundThread
{-# INLINABLE runInBoundThread #-}

-- | Generalized version of 'C.runInUnboundThread'.
runInUnboundThread  MonadBaseControl IO m  m a  m a
runInUnboundThread = liftBaseOp_ C.runInUnboundThread
{-# INLINABLE runInUnboundThread #-}

#if MIN_VERSION_base(4,6,0)
-- | Generalized versio  of 'C.mkWeakThreadId'.
mkWeakThreadId  MonadBase IO m  ThreadId  m (Weak ThreadId)
mkWeakThreadId = liftBase  C.mkWeakThreadId
{-# INLINABLE mkWeakThreadId #-}
#endif