module Control.Effect.Mask
  ( -- * Effects
    Mask(..)
  , MaskMode(..)

    -- * Actions
  , mask
  , mask_
  , uninterruptibleMask
  , uninterruptibleMask_

    -- * Interpretations
  , maskToIO

  , ignoreMask

    -- * Threading utilities
  , threadMaskViaClass

    -- * MonadMask
  , C.MonadMask

    -- * Carriers
  , MaskToIOC
  , IgnoreMaskC
  ) where

import Control.Effect
import Control.Effect.Primitive
import Control.Effect.Type.Mask

import Control.Monad.Catch (MonadMask)
import qualified Control.Monad.Catch as C

mask :: Eff Mask m => ((forall x. m x -> m x) -> m a) -> m a
mask main = send (Mask InterruptibleMask main)
{-# INLINE mask #-}

mask_ :: Eff Mask m => m a -> m a
mask_ main = mask $ \_ -> main
{-# INLINE mask_ #-}

uninterruptibleMask :: Eff Mask m => ((forall x. m x -> m x) -> m a) -> m a
uninterruptibleMask main = send (Mask UninterruptibleMask main)
{-# INLINE uninterruptibleMask #-}

uninterruptibleMask_ :: Eff Mask m => m a -> m a
uninterruptibleMask_ main = uninterruptibleMask $ \_ -> main
{-# INLINE uninterruptibleMask_ #-}

data MaskToIOH

instance ( Carrier m
         , MonadMask m
         )
      => PrimHandler MaskToIOH Mask m where
  effPrimHandler (Mask InterruptibleMask main)   = C.mask main
  effPrimHandler (Mask UninterruptibleMask main) = C.uninterruptibleMask main
  {-# INLINEABLE effPrimHandler #-}

type MaskToIOC = InterpretPrimC MaskToIOH Mask

-- | Run a 'Mask' effect by making use of the 'IO'-based 'Control.Exception.mask' and
-- 'Control.Exception.uninterruptibleMask'.
--
-- @'Derivs' ('MaskToIOC' m) = 'Mask' ': 'Derivs' m@
--
-- @'Prims'  ('MaskToIOC' m) = 'Mask' ': 'Prims' m@
maskToIO :: ( Carrier m
            , MonadMask m
            )
         => MaskToIOC m a
         -> m a
maskToIO = interpretPrimViaHandler
{-# INLINE maskToIO #-}

data IgnoreMaskH

instance Carrier m
      => Handler IgnoreMaskH Mask m where
  effHandler (Mask _ main) = main id
  {-# INLINEABLE effHandler #-}

type IgnoreMaskC = InterpretC IgnoreMaskH Mask

-- | Run a 'Mask' effect by ignoring it, providing no protection
-- against asynchronous exceptions.
--
-- @'Derivs' ('IgnoreMaskC' m) = 'Mask' ': 'Derivs' m@
--
-- @'Prims'  ('IgnoreMaskC' m) = 'Prims' m@
ignoreMask :: Carrier m
           => IgnoreMaskC m a
           -> m a
ignoreMask = interpretViaHandler
{-# INLINE ignoreMask #-}