{-# LANGUAGE MagicHash, UnboxedTuples, DeriveDataTypeable, CPP #-}

-- |
-- Module      : Data.Primitive.MutVar
-- Copyright   : (c) Justin Bonnar 2011, Roman Leshchinskiy 2011-2012
-- License     : BSD-style
--
-- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
-- Portability : non-portable
--
-- Primitive boxed mutable variables. This is a generalization of
-- "Data.IORef", "Data.STRef" and "Data.STRef.Lazy" to work in
-- any 'PrimMonad'.

module Data.Primitive.MutVar (
  MutVar(..),

  newMutVar,
  readMutVar,
  writeMutVar,

  atomicModifyMutVar,
  atomicModifyMutVar',
  modifyMutVar,
  modifyMutVar'
) where

import Control.Monad.Primitive ( PrimMonad(..), primitive_ )
import GHC.Exts ( MutVar#, sameMutVar#, newMutVar#
                , readMutVar#, writeMutVar#, atomicModifyMutVar#
                , isTrue# )
import Data.Typeable ( Typeable )

-- | A 'MutVar' behaves like a single-element mutable array associated
-- with a primitive state token.
data MutVar s a = MutVar (MutVar# s a)
  deriving ( Typeable )

instance Eq (MutVar s a) where
  MutVar MutVar# s a
mva# == :: MutVar s a -> MutVar s a -> Bool
== MutVar MutVar# s a
mvb# = Int# -> Bool
isTrue# (MutVar# s a -> MutVar# s a -> Int#
forall d a. MutVar# d a -> MutVar# d a -> Int#
sameMutVar# MutVar# s a
mva# MutVar# s a
mvb#)

-- | Create a new 'MutVar' with the specified initial value.
newMutVar :: PrimMonad m => a -> m (MutVar (PrimState m) a)
{-# INLINE newMutVar #-}
newMutVar :: a -> m (MutVar (PrimState m) a)
newMutVar a
initialValue = (State# (PrimState m)
 -> (# State# (PrimState m), MutVar (PrimState m) a #))
-> m (MutVar (PrimState m) a)
forall (m :: * -> *) a.
PrimMonad m =>
(State# (PrimState m) -> (# State# (PrimState m), a #)) -> m a
primitive ((State# (PrimState m)
  -> (# State# (PrimState m), MutVar (PrimState m) a #))
 -> m (MutVar (PrimState m) a))
-> (State# (PrimState m)
    -> (# State# (PrimState m), MutVar (PrimState m) a #))
-> m (MutVar (PrimState m) a)
forall a b. (a -> b) -> a -> b
$ \State# (PrimState m)
s# ->
  case a
-> State# (PrimState m)
-> (# State# (PrimState m), MutVar# (PrimState m) a #)
forall a d. a -> State# d -> (# State# d, MutVar# d a #)
newMutVar# a
initialValue State# (PrimState m)
s# of
    (# State# (PrimState m)
s'#, MutVar# (PrimState m) a
mv# #) -> (# State# (PrimState m)
s'#, MutVar# (PrimState m) a -> MutVar (PrimState m) a
forall s a. MutVar# s a -> MutVar s a
MutVar MutVar# (PrimState m) a
mv# #)

-- | Read the value of a 'MutVar'.
readMutVar :: PrimMonad m => MutVar (PrimState m) a -> m a
{-# INLINE readMutVar #-}
readMutVar :: MutVar (PrimState m) a -> m a
readMutVar (MutVar MutVar# (PrimState m) a
mv#) = (State# (PrimState m) -> (# State# (PrimState m), a #)) -> m a
forall (m :: * -> *) a.
PrimMonad m =>
(State# (PrimState m) -> (# State# (PrimState m), a #)) -> m a
primitive (MutVar# (PrimState m) a
-> State# (PrimState m) -> (# State# (PrimState m), a #)
forall d a. MutVar# d a -> State# d -> (# State# d, a #)
readMutVar# MutVar# (PrimState m) a
mv#)

-- | Write a new value into a 'MutVar'.
writeMutVar :: PrimMonad m => MutVar (PrimState m) a -> a -> m ()
{-# INLINE writeMutVar #-}
writeMutVar :: MutVar (PrimState m) a -> a -> m ()
writeMutVar (MutVar MutVar# (PrimState m) a
mv#) a
newValue = (State# (PrimState m) -> State# (PrimState m)) -> m ()
forall (m :: * -> *).
PrimMonad m =>
(State# (PrimState m) -> State# (PrimState m)) -> m ()
primitive_ (MutVar# (PrimState m) a
-> a -> State# (PrimState m) -> State# (PrimState m)
forall d a. MutVar# d a -> a -> State# d -> State# d
writeMutVar# MutVar# (PrimState m) a
mv# a
newValue)

-- | Atomically mutate the contents of a 'MutVar'.
--
-- This function is useful for using 'MutVar' in a safe way in a multithreaded program.
-- If you only have one 'MutVar', then using 'atomicModifyMutVar' to access and modify
-- it will prevent race conditions.
--
-- Extending the atomicity to multiple 'MutVar's is problematic,
-- so if you need to do anything more complicated,
-- using 'Data.Primitive.MVar.MVar' instead is a good idea.
--
-- 'atomicModifyMutVar' does not apply the function strictly. This means if a program
-- calls 'atomicModifyMutVar' many times, but seldom uses the value, thunks will pile up
-- in memory resulting in a space leak.
-- To avoid this problem, use 'atomicModifyMutVar'' instead.
atomicModifyMutVar :: PrimMonad m => MutVar (PrimState m) a -> (a -> (a, b)) -> m b
{-# INLINE atomicModifyMutVar #-}
atomicModifyMutVar :: MutVar (PrimState m) a -> (a -> (a, b)) -> m b
atomicModifyMutVar (MutVar MutVar# (PrimState m) a
mv#) a -> (a, b)
f = (State# (PrimState m) -> (# State# (PrimState m), b #)) -> m b
forall (m :: * -> *) a.
PrimMonad m =>
(State# (PrimState m) -> (# State# (PrimState m), a #)) -> m a
primitive ((State# (PrimState m) -> (# State# (PrimState m), b #)) -> m b)
-> (State# (PrimState m) -> (# State# (PrimState m), b #)) -> m b
forall a b. (a -> b) -> a -> b
$ MutVar# (PrimState m) a
-> (a -> (a, b))
-> State# (PrimState m)
-> (# State# (PrimState m), b #)
forall s a b c.
MutVar# s a -> (a -> b) -> State# s -> (# State# s, c #)
atomicModifyMutVar# MutVar# (PrimState m) a
mv# a -> (a, b)
f

-- | Strict version of 'atomicModifyMutVar'. This forces both the value stored
-- in the 'MutVar' as well as the value returned.
atomicModifyMutVar' :: PrimMonad m => MutVar (PrimState m) a -> (a -> (a, b)) -> m b
{-# INLINE atomicModifyMutVar' #-}
atomicModifyMutVar' :: MutVar (PrimState m) a -> (a -> (a, b)) -> m b
atomicModifyMutVar' MutVar (PrimState m) a
mv a -> (a, b)
f = do
  b
b <- MutVar (PrimState m) a -> (a -> (a, b)) -> m b
forall (m :: * -> *) a b.
PrimMonad m =>
MutVar (PrimState m) a -> (a -> (a, b)) -> m b
atomicModifyMutVar MutVar (PrimState m) a
mv a -> (a, b)
force
  b
b b -> m b -> m b
`seq` b -> m b
forall (m :: * -> *) a. Monad m => a -> m a
return b
b
  where
    force :: a -> (a, b)
force a
x = case a -> (a, b)
f a
x of
                v :: (a, b)
v@(a
x', b
_) -> a
x' a -> (a, b) -> (a, b)
`seq` (a, b)
v

-- | Mutate the contents of a 'MutVar'.
--
-- 'modifyMutVar' does not apply the function strictly. This means if a program
-- calls 'modifyMutVar' many times, but seldom uses the value, thunks will pile up
-- in memory resulting in a space leak.
-- To avoid this problem, use 'modifyMutVar'' instead.
modifyMutVar :: PrimMonad m => MutVar (PrimState m) a -> (a -> a) -> m ()
{-# INLINE modifyMutVar #-}
modifyMutVar :: MutVar (PrimState m) a -> (a -> a) -> m ()
modifyMutVar (MutVar MutVar# (PrimState m) a
mv#) a -> a
g = (State# (PrimState m) -> State# (PrimState m)) -> m ()
forall (m :: * -> *).
PrimMonad m =>
(State# (PrimState m) -> State# (PrimState m)) -> m ()
primitive_ ((State# (PrimState m) -> State# (PrimState m)) -> m ())
-> (State# (PrimState m) -> State# (PrimState m)) -> m ()
forall a b. (a -> b) -> a -> b
$ \State# (PrimState m)
s# ->
  case MutVar# (PrimState m) a
-> State# (PrimState m) -> (# State# (PrimState m), a #)
forall d a. MutVar# d a -> State# d -> (# State# d, a #)
readMutVar# MutVar# (PrimState m) a
mv# State# (PrimState m)
s# of
    (# State# (PrimState m)
s'#, a
a #) -> MutVar# (PrimState m) a
-> a -> State# (PrimState m) -> State# (PrimState m)
forall d a. MutVar# d a -> a -> State# d -> State# d
writeMutVar# MutVar# (PrimState m) a
mv# (a -> a
g a
a) State# (PrimState m)
s'#

-- | Strict version of 'modifyMutVar'.
modifyMutVar' :: PrimMonad m => MutVar (PrimState m) a -> (a -> a) -> m ()
{-# INLINE modifyMutVar' #-}
modifyMutVar' :: MutVar (PrimState m) a -> (a -> a) -> m ()
modifyMutVar' (MutVar MutVar# (PrimState m) a
mv#) a -> a
g = (State# (PrimState m) -> State# (PrimState m)) -> m ()
forall (m :: * -> *).
PrimMonad m =>
(State# (PrimState m) -> State# (PrimState m)) -> m ()
primitive_ ((State# (PrimState m) -> State# (PrimState m)) -> m ())
-> (State# (PrimState m) -> State# (PrimState m)) -> m ()
forall a b. (a -> b) -> a -> b
$ \State# (PrimState m)
s# ->
  case MutVar# (PrimState m) a
-> State# (PrimState m) -> (# State# (PrimState m), a #)
forall d a. MutVar# d a -> State# d -> (# State# d, a #)
readMutVar# MutVar# (PrimState m) a
mv# State# (PrimState m)
s# of
    (# State# (PrimState m)
s'#, a
a #) -> let a' :: a
a' = a -> a
g a
a in a
a' a -> State# (PrimState m) -> State# (PrimState m)
`seq` MutVar# (PrimState m) a
-> a -> State# (PrimState m) -> State# (PrimState m)
forall d a. MutVar# d a -> a -> State# d -> State# d
writeMutVar# MutVar# (PrimState m) a
mv# a
a' State# (PrimState m)
s'#