{-# LANGUAGE Unsafe #-}
{-|

This module implements the core of labeled 'MVars's in the 'LIO ad.
to "Control.Concurrent.MVar", but the operations take place in the
'LIO' monad.  The types and functions exported by this module are
strictly TCB and do not perform any information flow checks. The
external, safe interface is provided and documented in
"LIO.Concurrent.LMVar".

-}
module LIO.Concurrent.LMVar.TCB (
    LMVar(..)
  -- * Creating labeled 'MVar's
  , newEmptyLMVarTCB, newLMVarTCB
  -- * Take 'LMVar'
  , takeLMVarTCB, tryTakeLMVarTCB
  -- * Put 'LMVar'
  , putLMVarTCB, tryPutLMVarTCB
  -- * Read 'LMVar'
  , readLMVarTCB
  -- * Swap 'LMVar'
  , swapLMVarTCB
  -- * Check state of 'LMVar'
  , isEmptyLMVarTCB
  ) where

import           Control.Concurrent.MVar
                 
import           LIO.Label
import           LIO.Core
import           LIO.TCB

-- | An @LMVar@ is a labeled synchronization variable (an 'MVar') that
-- can be used by concurrent threads to communicate.
data LMVar l a = LMVarTCB { labelOfLMVar :: !l
                            -- ^ Label of MVar.
                          , unlabelLMVarTCB :: MVar a
                            -- ^ Access the underlying 'MVar', ignoring IFC.
                          }

instance LabelOf LMVar where
  labelOf = labelOfLMVar

--
-- Creating labeled 'MVar's
--

-- | Trusted function used to create an empty @LMVar@, ignoring IFC.
newEmptyLMVarTCB :: MonadLIO l m => l -> m (LMVar l a)
newEmptyLMVarTCB l = do
  m <- liftLIO . ioTCB $ newEmptyMVar
  return $ LMVarTCB l m

-- | Trusted function used to create an @LMVar@ with the supplied
-- value, ignoring IFC.
newLMVarTCB :: MonadLIO l m => l -> a -> m (LMVar l a)
newLMVarTCB l a = do
  m <- liftLIO . ioTCB $ newMVar a
  return $ LMVarTCB l m

--
-- Take 'LMVar'
--

-- | Read the contents of an 'LMVar', ignoring IFC.
takeLMVarTCB :: MonadLIO l m => LMVar l a -> m a
takeLMVarTCB (LMVarTCB _ m) = liftLIO . ioTCB $ takeMVar m

-- | Same as 'tryTakeLMVar', but ignorses IFC.
tryTakeLMVarTCB :: MonadLIO l m => LMVar l a -> m (Maybe a)
tryTakeLMVarTCB (LMVarTCB _ m) = liftLIO . ioTCB $ tryTakeMVar m

--
-- Put 'LMVar'
--

-- | Put a value into an 'LMVar', ignoring IFC.
putLMVarTCB :: MonadLIO l m => LMVar l a -> a -> m ()
putLMVarTCB (LMVarTCB _ m) a = liftLIO . ioTCB $ putMVar m a

-- | Same as 'tryPutLMVar', but ignorses IFC.
tryPutLMVarTCB :: MonadLIO l m => LMVar l a -> a -> m Bool
tryPutLMVarTCB (LMVarTCB _ m) x = liftLIO . ioTCB $ tryPutMVar m x


--
-- Read 'LMVar'
--

-- | Trusted function used to read (take and put) an 'LMVar', ignoring IFC.
readLMVarTCB :: MonadLIO l m => LMVar l a -> m a
readLMVarTCB (LMVarTCB _ m) = liftLIO . ioTCB $ readMVar m

--
-- Swap 'LMVar'
--

-- | Trusted function that swaps value of 'LMVar', ignoring IFC.
swapLMVarTCB :: MonadLIO l m => LMVar l a -> a -> m a
swapLMVarTCB (LMVarTCB _ m) x = liftLIO . ioTCB $ swapMVar m x

--
-- Check state of 'LMVar'
--

-- | Same as 'isEmptyLMVar', but ignorses IFC.
isEmptyLMVarTCB :: MonadLIO l m => LMVar l a -> m Bool
isEmptyLMVarTCB (LMVarTCB _ m) = liftLIO . ioTCB $ isEmptyMVar m