-- |
-- Module      : Control.Concurrent.Classy.CRef
-- Copyright   : (c) 2016--2018 Michael Walker
-- License     : MIT
-- Maintainer  : Michael Walker <mike@barrucadu.co.uk>
-- Stability   : experimental
-- Portability : portable
--
-- Deprecated re-exports of @IORef@ functions under the old @CRef@
-- names.
module Control.Concurrent.Classy.CRef {-# DEPRECATED "Import Control.Concurrent.Classy.IORef instead" #-}
  ( -- * CRefs
    CRef
  , newCRef
  , newCRefN
  , readCRef
  , writeCRef
  , modifyCRef
  , modifyCRef'
  , atomicModifyCRef
  , atomicModifyCRef'
  , atomicWriteCRef

  -- ** Compare-and-swap
  , casCRef
  , modifyCRefCAS
  , modifyCRefCAS_

  -- * Memory Model

  -- | In a concurrent program, @CRef@ operations may appear
  -- out-of-order to another thread, depending on the memory model of
  -- the underlying processor architecture. For example, on x86 (which
  -- uses total store order), loads can move ahead of stores. Consider
  -- this example:
  --
  -- > crefs :: MonadConc m => m (Bool, Bool)
  -- > crefs = do
  -- >   r1 <- newCRef False
  -- >   r2 <- newCRef False
  -- >
  -- >   x <- spawn $ writeCRef r1 True >> readCRef r2
  -- >   y <- spawn $ writeCRef r2 True >> readCRef r1
  -- >
  -- >   (,) <$> readMVar x <*> readMVar y
  --
  -- Under a sequentially consistent memory model the possible results
  -- are @(True, True)@, @(True, False)@, and @(False, True)@. Under
  -- total or partial store order, @(False, False)@ is also a possible
  -- result, even though there is no interleaving of the threads which
  -- can lead to this.
  --
  -- We can see this by testing with different memory models:
  --
  -- > > autocheckWay defaultWay SequentialConsistency relaxed
  -- > [pass] Never Deadlocks
  -- > [pass] No Exceptions
  -- > [fail] Consistent Result
  -- >        (False,True) S0---------S1----S0--S2----S0--
  -- >
  -- >        (True,True) S0---------S1-P2----S1---S0---
  -- >
  -- >        (True,False) S0---------S2----S1----S0---
  -- > False
  --
  -- > > autocheckWay defaultWay TotalStoreOrder  relaxed
  -- > [pass] Never Deadlocks
  -- > [pass] No Exceptions
  -- > [fail] Consistent Result
  -- >         (False,True) S0---------S1----S0--S2----S0--
  -- >
  -- >         (False,False) S0---------S1--P2----S1--S0---
  -- >
  -- >         (True,False) S0---------S2----S1----S0---
  -- >
  -- >         (True,True) S0---------S1-C-S2----S1---S0---
  -- > False
  --
  -- Traces for non-sequentially-consistent memory models show where
  -- writes to @CRef@s are /committed/, which makes a write visible to
  -- all threads rather than just the one which performed the
  -- write. Only 'writeCRef' is broken up into separate write and
  -- commit steps, 'atomicModifyCRef' is still atomic and imposes a
  -- memory barrier.
  ) where

import qualified Control.Concurrent.Classy.IORef as IORef
import           Control.Monad.Conc.Class        (IORef, MonadConc, Ticket)
import qualified Control.Monad.Conc.Class        as IORef

-- | Type alias for 'IORef'.
type CRef m a = IORef m a
{-# DEPRECATED CRef "Use IORef instead" #-}

-- | Create a new reference.
newCRef :: MonadConc m => a -> m (CRef m a)
newCRef = IORef.newIORef
{-# DEPRECATED newCRef "Use newIORef instead" #-}

-- | Create a new reference, but it is given a name which may be used
-- to present more useful debugging information.
newCRefN :: MonadConc m => String -> a -> m (CRef m a)
newCRefN = IORef.newIORefN
{-# DEPRECATED newCRefN "Use newIORefN instead" #-}

-- | Read the current value stored in a reference.
readCRef :: MonadConc m => CRef m a -> m a
readCRef = IORef.readIORef
{-# DEPRECATED readCRef "Use readIORef instead" #-}

-- | Write a new value into an @CRef@, without imposing a memory
-- barrier. This means that relaxed memory effects can be observed.
writeCRef :: MonadConc m => CRef m a -> a -> m ()
writeCRef = IORef.writeIORef
{-# DEPRECATED writeCRef "Use writeIORef instead" #-}

-- | Mutate the contents of a @CRef@.
--
-- Be warned that 'modifyCRef' does not apply the function strictly.
-- This means if the program calls 'modifyCRef' many times, but
-- seldomly uses the value, thunks will pile up in memory resulting in
-- a space leak.
modifyCRef :: MonadConc m => CRef m a -> (a -> a) -> m ()
modifyCRef = IORef.modifyIORef
{-# DEPRECATED modifyCRef "Use modifyIORef instead" #-}

-- | Strict version of 'modifyCRef'
modifyCRef' :: MonadConc m => CRef m a -> (a -> a) -> m ()
modifyCRef' = IORef.modifyIORef'
{-# DEPRECATED modifyCRef' "Use modifyIORef' instead" #-}

-- | Atomically modify the value stored in a reference. This imposes
-- a full memory barrier.
atomicModifyCRef :: MonadConc m => CRef m a -> (a -> (a, b)) -> m b
atomicModifyCRef = IORef.atomicModifyIORef
{-# DEPRECATED atomicModifyCRef "Use atomicModifyIORef instead" #-}

-- | Strict version of 'atomicModifyCRef'. This forces both the value
-- stored in the @CRef@ as well as the value returned.
atomicModifyCRef' :: MonadConc m => CRef m a -> (a -> (a,b)) -> m b
atomicModifyCRef' = IORef.atomicModifyIORef'
{-# DEPRECATED atomicModifyCRef' "Use atomicModifyIORef' instead" #-}

-- | Replace the value stored in a reference, with the
-- barrier-to-reordering property that 'atomicModifyIORef' has.
atomicWriteCRef :: MonadConc m => CRef m a -> a -> m ()
atomicWriteCRef = IORef.atomicWriteIORef
{-# DEPRECATED atomicWriteCRef "Use atomicWriteIORef instead" #-}

-- | Perform a machine-level compare-and-swap (CAS) operation on a
-- @CRef@. Returns an indication of success and a @Ticket@ for the
-- most current value in the @CRef@.
-- This is strict in the \"new\" value argument.
casCRef :: MonadConc m => CRef m a -> Ticket m a -> a -> m (Bool, Ticket m a)
casCRef = IORef.casIORef
{-# DEPRECATED casCRef "Use casIORef instead" #-}

-- | A replacement for 'atomicModifyCRef' using a compare-and-swap.
--
-- This is strict in the \"new\" value argument.
modifyCRefCAS :: MonadConc m => CRef m a -> (a -> (a, b)) -> m b
modifyCRefCAS = IORef.modifyIORefCAS
{-# DEPRECATED modifyCRefCAS "Use modifyIORefCAS instead" #-}

-- | A variant of 'modifyCRefCAS' which doesn't return a result.
modifyCRefCAS_ :: MonadConc m => CRef m a -> (a -> a) -> m ()
modifyCRefCAS_ = IORef.modifyIORefCAS_
{-# DEPRECATED modifyCRefCAS_ "Use modifyIORefCAS_ instead" #-}